From 6006bed530b41e51e9ceccb04849a290496c6bfd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 25 May 2007 16:01:31 +0000 Subject: [PATCH 01/15] Free I/O buffers when we are finished with them! --- src/core/posix_io.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/posix_io.c b/src/core/posix_io.c index b98766f8..6cefbf7b 100644 --- a/src/core/posix_io.c +++ b/src/core/posix_io.c @@ -78,6 +78,7 @@ static void posix_file_free ( struct refcnt *refcnt ) { struct io_buffer *tmp; list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) { + list_del ( &iobuf->list ); free_iob ( iobuf ); } free ( file ); @@ -279,8 +280,10 @@ ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) { copy_to_user ( buffer, offset, iobuf->data, frag_len ); iob_pull ( iobuf, frag_len ); - if ( ! iob_len ( iobuf ) ) + if ( ! iob_len ( iobuf ) ) { + list_del ( &iobuf-> list ); free_iob ( iobuf ); + } file->pos += frag_len; len += frag_len; offset += frag_len; From 360110338147844bfa4612ae2d6351f1d1054626 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 26 May 2007 15:00:56 +0000 Subject: [PATCH 02/15] Modify process semantics; rescheduling is now automatic. Add reference-counting to processes. Add timer_running() test. --- src/core/process.c | 26 ++++++++++++++++++++---- src/include/gpxe/process.h | 41 ++++++++++++++++++++++++++++++++++---- src/include/gpxe/retry.h | 11 ++++++++++ src/net/netdevice.c | 7 ++----- src/net/retry.c | 10 ++++------ 5 files changed, 76 insertions(+), 19 deletions(-) diff --git a/src/core/process.c b/src/core/process.c index c087f16d..0583a398 100644 --- a/src/core/process.c +++ b/src/core/process.c @@ -31,25 +31,43 @@ static LIST_HEAD ( run_queue ); /** - * Add process to run queue + * Add process to process list * * @v process Process */ -void schedule ( struct process *process ) { +void process_add ( struct process *process ) { + ref_get ( process->refcnt ); list_add_tail ( &process->list, &run_queue ); } +/** + * Remove process from process list + * + * @v process Process + * + * It is safe to call process_del() multiple times; further calls will + * have no effect. + */ +void process_del ( struct process *process ) { + if ( ! list_empty ( &process->list ) ) { + list_del ( &process->list ); + INIT_LIST_HEAD ( &process->list ); + ref_put ( process->refcnt ); + } +} + /** * Single-step a single process * - * This removes the first process from the run queue and executes a - * single step of that process. + * This executes a single step of the first process in the run queue, + * and moves the process to the end of the run queue. */ void step ( void ) { struct process *process; list_for_each_entry ( process, &run_queue, list ) { list_del ( &process->list ); + list_add_tail ( &process->list, &run_queue ); process->step ( process ); break; } diff --git a/src/include/gpxe/process.h b/src/include/gpxe/process.h index 83ff8393..c0837fa4 100644 --- a/src/include/gpxe/process.h +++ b/src/include/gpxe/process.h @@ -8,6 +8,7 @@ */ #include +#include /** A process */ struct process { @@ -19,14 +20,46 @@ struct process { * This method should execute a single step of the process. * Returning from this method is isomorphic to yielding the * CPU to another process. - * - * If the process wishes to be executed again, it must re-add - * itself to the run queue using schedule(). */ void ( * step ) ( struct process *process ); + /** Reference counter + * + * If this interface is not part of a reference-counted + * object, this field may be NULL. + */ + struct refcnt *refcnt; }; -extern void schedule ( struct process *process ); +extern void process_add ( struct process *process ); +extern void process_del ( struct process *process ); extern void step ( void ); +/** + * Initialise process without adding to process list + * + * @v process Process + * @v step Process' step() method + */ +static inline __attribute__ (( always_inline )) void +process_init_stopped ( struct process *process, + void ( * step ) ( struct process *process ), + struct refcnt *refcnt ) { + process->step = step; + process->refcnt = refcnt; +} + +/** + * Initialise process and add to process list + * + * @v process Process + * @v step Process' step() method + */ +static inline __attribute__ (( always_inline )) void +process_init ( struct process *process, + void ( * step ) ( struct process *process ), + struct refcnt *refcnt ) { + process_init_stopped ( process, step, refcnt ); + process_add ( process ); +} + #endif /* _GPXE_PROCESS_H */ diff --git a/src/include/gpxe/retry.h b/src/include/gpxe/retry.h index 57be432e..e0c0248b 100644 --- a/src/include/gpxe/retry.h +++ b/src/include/gpxe/retry.h @@ -37,4 +37,15 @@ struct retry_timer { extern void start_timer ( struct retry_timer *timer ); extern void stop_timer ( struct retry_timer *timer ); +/** + * Test to see if timer is currently running + * + * @v timer Retry timer + * @ret running Non-zero if timer is running + */ +static inline __attribute__ (( always_inline )) unsigned long +timer_running ( struct retry_timer *timer ) { + return ( timer->start ); +} + #endif /* _GPXE_RETRY_H */ diff --git a/src/net/netdevice.c b/src/net/netdevice.c index c1ad5f68..ddcb296f 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -388,7 +388,7 @@ int net_rx ( struct io_buffer *iobuf, struct net_device *netdev, * This polls all interfaces for received packets, and processes * packets from the RX queue. */ -static void net_step ( struct process *process ) { +static void net_step ( struct process *process __unused ) { struct net_device *netdev; struct io_buffer *iobuf; @@ -410,9 +410,6 @@ static void net_step ( struct process *process ) { netdev->ll_protocol->rx ( iobuf, netdev ); } } - - /* Re-schedule ourself */ - schedule ( process ); } /** Networking stack process */ @@ -422,7 +419,7 @@ static struct process net_process = { /** Initialise the networking stack process */ static void init_net ( void ) { - schedule ( &net_process ); + process_add ( &net_process ); } INIT_FN ( INIT_PROCESS, init_net, NULL, NULL ); diff --git a/src/net/retry.c b/src/net/retry.c index cd683f5b..f8c34b81 100644 --- a/src/net/retry.c +++ b/src/net/retry.c @@ -64,7 +64,7 @@ static LIST_HEAD ( timers ); * be stopped and the timer's callback function will be called. */ void start_timer ( struct retry_timer *timer ) { - if ( ! timer->start ) + if ( ! timer_running ( timer ) ) list_add ( &timer->list, &timers ); timer->start = currticks(); if ( timer->timeout < MIN_TIMEOUT ) @@ -86,7 +86,7 @@ void stop_timer ( struct retry_timer *timer ) { unsigned long runtime; /* If timer was already stopped, do nothing */ - if ( ! timer->start ) + if ( ! timer_running ( timer ) ) return; list_del ( &timer->list ); @@ -153,7 +153,7 @@ static void timer_expired ( struct retry_timer *timer ) { * * @v process Retry timer process */ -static void retry_step ( struct process *process ) { +static void retry_step ( struct process *process __unused ) { struct retry_timer *timer; struct retry_timer *tmp; unsigned long now = currticks(); @@ -164,8 +164,6 @@ static void retry_step ( struct process *process ) { if ( used >= timer->timeout ) timer_expired ( timer ); } - - schedule ( process ); } /** Retry timer process */ @@ -175,7 +173,7 @@ static struct process retry_process = { /** Initialise the retry timer module */ static void init_retry ( void ) { - schedule ( &retry_process ); + process_add ( &retry_process ); } INIT_FN ( INIT_PROCESS, init_retry, NULL, NULL ); From 10d0a1f8c759309ad0aa8f73c87ae7b45cbb5fe6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 26 May 2007 15:04:36 +0000 Subject: [PATCH 03/15] Modify data-xfer semantics: it is no longer necessary to call one of request(), seek() or deliver_xxx() in order to start the data flow. Autonomous generators must be genuinely autonomous (having their own process), or otherwise arrange to be called. TCP does this by starting the retry timer immediately. Add some debugging statements. --- src/core/downloader.c | 15 +-------- src/core/hw.c | 27 +++++++++------- src/core/posix_io.c | 8 ++--- src/core/xfer.c | 71 +++++++++++++++++++++++++++++++++++------ src/include/gpxe/xfer.h | 16 +++++++++- 5 files changed, 96 insertions(+), 41 deletions(-) diff --git a/src/core/downloader.c b/src/core/downloader.c index 2f28bc95..15ef962d 100644 --- a/src/core/downloader.c +++ b/src/core/downloader.c @@ -114,19 +114,6 @@ static int downloader_ensure_size ( struct downloader *downloader, * */ -/** - * Handle start() event received via job control interface - * - * @v job Downloader job control interface - */ -static void downloader_job_start ( struct job_interface *job ) { - struct downloader *downloader = - container_of ( job, struct downloader, job ); - - /* Start data transfer */ - xfer_request_all ( &downloader->xfer ); -} - /** * Handle kill() event received via job control interface * @@ -142,7 +129,7 @@ static void downloader_job_kill ( struct job_interface *job ) { /** Downloader job control interface operations */ static struct job_interface_operations downloader_job_operations = { - .start = downloader_job_start, + .start = ignore_job_start, .done = ignore_job_done, .kill = downloader_job_kill, .progress = ignore_job_progress, diff --git a/src/core/hw.c b/src/core/hw.c index 77b39ba1..a3eb8500 100644 --- a/src/core/hw.c +++ b/src/core/hw.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -15,6 +16,7 @@ struct hw { struct refcnt refcnt; struct xfer_interface xfer; + struct process process; }; static const char hw_msg[] = "Hello world!\n"; @@ -22,6 +24,7 @@ static const char hw_msg[] = "Hello world!\n"; static void hw_finished ( struct hw *hw, int rc ) { xfer_nullify ( &hw->xfer ); xfer_close ( &hw->xfer, rc ); + process_del ( &hw->process ); } static void hw_xfer_close ( struct xfer_interface *xfer, int rc ) { @@ -30,26 +33,25 @@ static void hw_xfer_close ( struct xfer_interface *xfer, int rc ) { hw_finished ( hw, rc ); } -static int hw_xfer_request ( struct xfer_interface *xfer, - off_t start __unused, int whence __unused, - size_t len __unused ) { - struct hw *hw = container_of ( xfer, struct hw, xfer ); - int rc; - - rc = xfer_deliver_raw ( xfer, hw_msg, sizeof ( hw_msg ) ); - hw_finished ( hw, rc ); - return 0; -} - static struct xfer_interface_operations hw_xfer_operations = { .close = hw_xfer_close, .vredirect = ignore_xfer_vredirect, - .request = hw_xfer_request, + .request = ignore_xfer_request, .seek = ignore_xfer_seek, .deliver_iob = xfer_deliver_as_raw, .deliver_raw = ignore_xfer_deliver_raw, }; +static void hw_step ( struct process *process ) { + struct hw *hw = container_of ( process, struct hw, process ); + int rc; + + if ( xfer_ready ( &hw->xfer ) == 0 ) { + rc = xfer_deliver_raw ( &hw->xfer, hw_msg, sizeof ( hw_msg ) ); + hw_finished ( hw, rc ); + } +} + static int hw_open ( struct xfer_interface *xfer, struct uri *uri __unused ) { struct hw *hw; @@ -59,6 +61,7 @@ static int hw_open ( struct xfer_interface *xfer, struct uri *uri __unused ) { return -ENOMEM; memset ( hw, 0, sizeof ( *hw ) ); xfer_init ( &hw->xfer, &hw_xfer_operations, &hw->refcnt ); + process_init ( &hw->process, hw_step, &hw->refcnt ); /* Attach parent interface, mortalise self, and return */ xfer_plug_plug ( &hw->xfer, xfer ); diff --git a/src/core/posix_io.c b/src/core/posix_io.c index 6cefbf7b..3b5660e4 100644 --- a/src/core/posix_io.c +++ b/src/core/posix_io.c @@ -191,6 +191,7 @@ static int posix_find_free_fd ( void ) { if ( ! posix_fd_to_file ( fd ) ) return fd; } + DBG ( "POSIX could not find free file descriptor\n" ); return -ENFILE; } @@ -226,13 +227,11 @@ int open ( const char *uri_string ) { if ( ( rc = xfer_open_uri ( &file->xfer, uri_string ) ) != 0 ) goto err; - /* Request data */ - if ( ( rc = xfer_request_all ( &file->xfer ) ) != 0 ) - goto err; - /* Wait for open to succeed or fail */ while ( list_empty ( &file->data ) ) { step(); + if ( file->rc == 0 ) + break; if ( file->rc != -EINPROGRESS ) { rc = file->rc; goto err; @@ -241,6 +240,7 @@ int open ( const char *uri_string ) { /* Add to list of open files. List takes reference ownership. */ list_add ( &file->list, &posix_files ); + DBG ( "POSIX opened %s as file %d\n", uri_string, fd ); return fd; err: diff --git a/src/core/xfer.c b/src/core/xfer.c index f2783f5b..54377c70 100644 --- a/src/core/xfer.c +++ b/src/core/xfer.c @@ -35,6 +35,8 @@ void xfer_close ( struct xfer_interface *xfer, int rc ) { struct xfer_interface *dest = xfer_get_dest ( xfer ); + DBGC ( xfer, "XFER %p->%p close\n", xfer, dest ); + dest->op->close ( dest, rc ); xfer_unplug ( xfer ); xfer_put ( dest ); @@ -52,7 +54,14 @@ int xfer_vredirect ( struct xfer_interface *xfer, int type, va_list args ) { struct xfer_interface *dest = xfer_get_dest ( xfer ); int rc; + DBGC ( xfer, "XFER %p->%p redirect\n", xfer, dest ); + rc = dest->op->vredirect ( dest, type, args ); + + if ( rc != 0 ) { + DBGC ( xfer, "XFER %p<-%p redirect: %s\n", xfer, dest, + strerror ( rc ) ); + } xfer_put ( dest ); return rc; } @@ -89,21 +98,19 @@ int xfer_request ( struct xfer_interface *xfer, off_t offset, int whence, struct xfer_interface *dest = xfer_get_dest ( xfer ); int rc; + DBGC ( xfer, "XFER %p->%p request %s+%ld %zd\n", xfer, dest, + whence_text ( whence ), offset, len ); + rc = dest->op->request ( dest, offset, whence, len ); + + if ( rc != 0 ) { + DBGC ( xfer, "XFER %p<-%p request: %s\n", xfer, dest, + strerror ( rc ) ); + } xfer_put ( dest ); return rc; } -/** - * Request all data - * - * @v xfer Data transfer interface - * @ret rc Return status code - */ -int xfer_request_all ( struct xfer_interface *xfer ) { - return xfer_request ( xfer, 0, SEEK_SET, ~( ( size_t ) 0 ) ); -} - /** * Seek to position * @@ -116,11 +123,33 @@ int xfer_seek ( struct xfer_interface *xfer, off_t offset, int whence ) { struct xfer_interface *dest = xfer_get_dest ( xfer ); int rc; + DBGC ( xfer, "XFER %p->%p seek %s+%ld\n", xfer, dest, + whence_text ( whence ), offset ); + rc = dest->op->seek ( dest, offset, whence ); + + if ( rc != 0 ) { + DBGC ( xfer, "XFER %p<-%p seek: %s\n", xfer, dest, + strerror ( rc ) ); + } xfer_put ( dest ); return rc; } +/** + * Test to see if interface is ready to accept data + * + * @v xfer Data transfer interface + * @ret rc Return status code + * + * This test is optional; the data transfer interface may wish that it + * does not yet wish to accept data, but cannot prevent attempts to + * deliver data to it. + */ +int xfer_ready ( struct xfer_interface *xfer ) { + return xfer_seek ( xfer, 0, SEEK_CUR ); +} + /** * Allocate I/O buffer * @@ -132,7 +161,13 @@ struct io_buffer * xfer_alloc_iob ( struct xfer_interface *xfer, size_t len ) { struct xfer_interface *dest = xfer_get_dest ( xfer ); struct io_buffer *iobuf; + DBGC ( xfer, "XFER %p->%p alloc_iob %zd\n", xfer, dest, len ); + iobuf = dest->op->alloc_iob ( dest, len ); + + if ( ! iobuf ) { + DBGC ( xfer, "XFER %p<-%p alloc_iob failed\n", xfer, dest ); + } xfer_put ( dest ); return iobuf; } @@ -148,7 +183,15 @@ int xfer_deliver_iob ( struct xfer_interface *xfer, struct io_buffer *iobuf ) { struct xfer_interface *dest = xfer_get_dest ( xfer ); int rc; + DBGC ( xfer, "XFER %p->%p deliver_iob %zd\n", xfer, dest, + iob_len ( iobuf ) ); + rc = dest->op->deliver_iob ( dest, iobuf ); + + if ( rc != 0 ) { + DBGC ( xfer, "XFER %p<-%p deliver_iob: %s\n", xfer, dest, + strerror ( rc ) ); + } xfer_put ( dest ); return rc; } @@ -165,7 +208,15 @@ int xfer_deliver_raw ( struct xfer_interface *xfer, struct xfer_interface *dest = xfer_get_dest ( xfer ); int rc; + DBGC ( xfer, "XFER %p->%p deliver_raw %p+%zd\n", xfer, dest, + data, len ); + rc = dest->op->deliver_raw ( dest, data, len ); + + if ( rc != 0 ) { + DBGC ( xfer, "XFER %p<-%p deliver_raw: %s\n", xfer, dest, + strerror ( rc ) ); + } xfer_put ( dest ); return rc; } diff --git a/src/include/gpxe/xfer.h b/src/include/gpxe/xfer.h index 71b69dc5..e6c896c3 100644 --- a/src/include/gpxe/xfer.h +++ b/src/include/gpxe/xfer.h @@ -112,6 +112,20 @@ enum seek_whence { SEEK_CUR, }; +/** + * Describe seek basis + * + * @v whence Basis for new position + */ +static inline __attribute__ (( always_inline )) const char * +whence_text ( int whence ) { + switch ( whence ) { + case SEEK_SET: return "SET"; + case SEEK_CUR: return "CUR"; + default: return "INVALID"; + } +} + extern struct xfer_interface null_xfer; extern struct xfer_interface_operations null_xfer_ops; @@ -121,8 +135,8 @@ extern int xfer_vredirect ( struct xfer_interface *xfer, int type, extern int xfer_redirect ( struct xfer_interface *xfer, int type, ... ); extern int xfer_request ( struct xfer_interface *xfer, off_t offset, int whence, size_t len ); -extern int xfer_request_all ( struct xfer_interface *xfer ); extern int xfer_seek ( struct xfer_interface *xfer, off_t offset, int whence ); +extern int xfer_ready ( struct xfer_interface *xfer ); extern struct io_buffer * xfer_alloc_iob ( struct xfer_interface *xfer, size_t len ); extern int xfer_deliver_iob ( struct xfer_interface *xfer, From 070c55a838fcfe86faca4e1ae412346772d61adc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 26 May 2007 15:05:31 +0000 Subject: [PATCH 04/15] Added EPIPE description. --- src/hci/strerror.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hci/strerror.c b/src/hci/strerror.c index 045e6357..7609f03e 100644 --- a/src/hci/strerror.c +++ b/src/hci/strerror.c @@ -120,4 +120,5 @@ struct errortab common_errors[] __errortab = { { ENOENT, "File not found" }, { ENETUNREACH, "Network unreachable" }, { ETIMEDOUT, "Connection timed out" }, + { EPIPE, "Broken pipe" }, }; From a1210e4fcab0e8dbf696444da13552f612701198 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 26 May 2007 20:41:23 +0000 Subject: [PATCH 05/15] Eliminate PF_INET; just use the AF_INET from the struct sockaddr instead. --- src/core/open.c | 24 +++++++++--------- src/include/gpxe/open.h | 18 ++++++++------ src/include/gpxe/socket.h | 51 ++++++++++++++++----------------------- 3 files changed, 43 insertions(+), 50 deletions(-) diff --git a/src/core/open.c b/src/core/open.c index 284d00a9..b61160eb 100644 --- a/src/core/open.c +++ b/src/core/open.c @@ -75,30 +75,29 @@ int xfer_open_uri ( struct xfer_interface *xfer, const char *uri_string ) { * Open socket * * @v xfer Data transfer interface - * @v domain Communication domain (e.g. PF_INET) - * @v type Communication semantics (e.g. SOCK_STREAM) + * @v semantics Communication semantics (e.g. SOCK_STREAM) * @v peer Peer socket address * @v local Local socket address, or NULL * @ret rc Return status code */ -int xfer_open_socket ( struct xfer_interface *xfer, - int domain, int type, struct sockaddr *peer, - struct sockaddr *local ) { +int xfer_open_socket ( struct xfer_interface *xfer, int semantics, + struct sockaddr *peer, struct sockaddr *local ) { struct socket_opener *opener; DBGC ( xfer, "XFER %p opening (%s,%s) socket\n", xfer, - socket_domain_name ( domain ), socket_type_name ( type ) ); + socket_semantics_name ( semantics ), + socket_family_name ( peer->sa_family ) ); for ( opener = socket_openers; opener < socket_openers_end; opener++ ){ - if ( ( opener->domain == domain ) && - ( opener->type == type ) ) { + if ( ( opener->semantics == semantics ) && + ( opener->family == peer->sa_family ) ) { return opener->open ( xfer, peer, local ); } } DBGC ( xfer, "XFER %p attempted to open unsupported socket type " - "(%s,%s)\n", xfer, socket_domain_name ( domain ), - socket_type_name ( type ) ); + "(%s,%s)\n", xfer, socket_semantics_name ( semantics ), + socket_family_name ( peer->sa_family ) ); return -ENOTSUP; } @@ -117,12 +116,11 @@ int xfer_vopen ( struct xfer_interface *xfer, int type, va_list args ) { return xfer_open_uri ( xfer, uri_string ); } case LOCATION_SOCKET: { - int domain = va_arg ( args, int ); - int type = va_arg ( args, int ); + int semantics = va_arg ( args, int ); struct sockaddr *peer = va_arg ( args, struct sockaddr * ); struct sockaddr *local = va_arg ( args, struct sockaddr * ); - return xfer_open_socket ( xfer, domain, type, peer, local ); } + return xfer_open_socket ( xfer, semantics, peer, local ); } default: DBGC ( xfer, "XFER %p attempted to open unsupported location " "type %d\n", xfer, type ); diff --git a/src/include/gpxe/open.h b/src/include/gpxe/open.h index 229d2d78..b16bbe88 100644 --- a/src/include/gpxe/open.h +++ b/src/include/gpxe/open.h @@ -26,7 +26,9 @@ enum { * * Parameter list for open() is: * - * + * int semantics; + * struct sockaddr *peer; + * struct sockaddr *local; */ LOCATION_SOCKET, }; @@ -56,10 +58,10 @@ struct uri_opener { /** A socket opener */ struct socket_opener { - /** Communication domain (e.g. PF_INET) */ - int domain; /** Communication semantics (e.g. SOCK_STREAM) */ - int type; + int semantics; + /** Address family (e.g. AF_INET) */ + int family; /** Open socket * * @v xfer Data transfer interface @@ -76,9 +78,11 @@ struct socket_opener { extern int xfer_open_uri ( struct xfer_interface *xfer, const char *uri_string ); -extern int xfer_open_socket ( struct xfer_interface *xfer, - int domain, int type, struct sockaddr *peer, - struct sockaddr *local ); +extern int xfer_open_named_socket ( struct xfer_interface *xfer, + int semantics, struct sockaddr *peer, + const char *name, struct sockaddr *local ); +extern int xfer_open_socket ( struct xfer_interface *xfer, int semantics, + struct sockaddr *peer, struct sockaddr *local ); extern int xfer_vopen ( struct xfer_interface *xfer, int type, va_list args ); extern int xfer_open ( struct xfer_interface *xfer, int type, ... ); diff --git a/src/include/gpxe/socket.h b/src/include/gpxe/socket.h index ea602537..d47369aa 100644 --- a/src/include/gpxe/socket.h +++ b/src/include/gpxe/socket.h @@ -8,31 +8,7 @@ */ /** - * @defgroup commdomains Communication domains - * - * @{ - */ -#define PF_INET 1 /**< IPv4 Internet protocols */ -#define PF_INET6 2 /**< IPv6 Internet protocols */ -/** @} */ - -/** - * Name communication domain - * - * @v domain Communication domain (e.g. PF_INET) - * @ret name Name of communication domain - */ -static inline __attribute__ (( always_inline )) const char * -socket_domain_name ( int domain ) { - switch ( domain ) { - case PF_INET: return "PF_INET"; - case PF_INET6: return "PF_INET6"; - default: return "PF_UNKNOWN"; - } -} - -/** - * @defgroup commtypes Communication types + * @defgroup commtypes Communication semantics * * @{ */ @@ -41,14 +17,14 @@ socket_domain_name ( int domain ) { /** @} */ /** - * Name communication type + * Name communication semantics * - * @v type Communication type (e.g. SOCK_STREAM) - * @ret name Name of communication type + * @v semantics Communication semantics (e.g. SOCK_STREAM) + * @ret name Name of communication semantics */ static inline __attribute__ (( always_inline )) const char * -socket_type_name ( int type ) { - switch ( type ) { +socket_semantics_name ( int semantics ) { + switch ( semantics ) { case SOCK_STREAM: return "SOCK_STREAM"; case SOCK_DGRAM: return "SOCK_DGRAM"; default: return "SOCK_UNKNOWN"; @@ -64,6 +40,21 @@ socket_type_name ( int type ) { #define AF_INET6 2 /**< IPv6 Internet addresses */ /** @} */ +/** + * Name address family + * + * @v family Address family (e.g. AF_INET) + * @ret name Name of address family + */ +static inline __attribute__ (( always_inline )) const char * +socket_family_name ( int family ) { + switch ( family ) { + case AF_INET: return "AF_INET"; + case AF_INET6: return "AF_INET6"; + default: return "AF_UNKNOWN"; + } +} + /** A socket address family */ typedef uint16_t sa_family_t; From bc694ec768bfdc2bc972828280135b0c6acf5071 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 27 May 2007 01:04:52 +0000 Subject: [PATCH 06/15] Move increment/decrement debug messages to DBG2 level --- src/core/refcnt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/refcnt.c b/src/core/refcnt.c index 227dac3f..36b7ce22 100644 --- a/src/core/refcnt.c +++ b/src/core/refcnt.c @@ -39,8 +39,8 @@ void ref_get ( struct refcnt *refcnt ) { refcnt->refcnt++; - DBGC ( refcnt, "REFCNT %p incremented to %d\n", - refcnt, refcnt->refcnt ); + DBGC2 ( refcnt, "REFCNT %p incremented to %d\n", + refcnt, refcnt->refcnt ); } /** @@ -59,8 +59,8 @@ void ref_put ( struct refcnt *refcnt ) { return; refcnt->refcnt--; - DBGC ( refcnt, "REFCNT %p decremented to %d\n", - refcnt, refcnt->refcnt ); + DBGC2 ( refcnt, "REFCNT %p decremented to %d\n", + refcnt, refcnt->refcnt ); if ( refcnt->refcnt >= 0 ) return; From 656485c1f1487775ccb0c223c4f5809f8daa7fcc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 28 May 2007 17:35:15 +0000 Subject: [PATCH 07/15] Make URI structures reference-counted. --- src/core/download.c | 4 ++-- src/core/open.c | 9 ++++++--- src/core/uri.c | 2 +- src/include/gpxe/open.h | 3 --- src/include/gpxe/uri.h | 25 +++++++++++++++++++------ 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/core/download.c b/src/core/download.c index 4522bf2c..e3f77794 100644 --- a/src/core/download.c +++ b/src/core/download.c @@ -121,7 +121,7 @@ int start_download ( const char *uri_string, struct async *parent, err: async_uninit ( &download->async ); ufree ( download->buffer.addr ); - free_uri ( download->uri ); + uri_put ( download->uri ); free ( download ); return rc; } @@ -150,7 +150,7 @@ static void download_sigchld ( struct async *async, /* Discard the buffer */ ufree ( download->buffer.addr ); } - free_uri ( download->uri ); + uri_put ( download->uri ); download->uri = NULL; /* Terminate ourselves */ diff --git a/src/core/open.c b/src/core/open.c index b61160eb..6c184e65 100644 --- a/src/core/open.c +++ b/src/core/open.c @@ -52,6 +52,7 @@ static struct socket_opener socket_openers_end[0] int xfer_open_uri ( struct xfer_interface *xfer, const char *uri_string ) { struct uri *uri; struct uri_opener *opener; + int rc = -ENOTSUP; DBGC ( xfer, "XFER %p opening URI %s\n", xfer, uri_string ); @@ -61,14 +62,16 @@ int xfer_open_uri ( struct xfer_interface *xfer, const char *uri_string ) { for ( opener = uri_openers ; opener < uri_openers_end ; opener++ ) { if ( strcmp ( uri->scheme, opener->scheme ) == 0 ) { - return opener->open ( xfer, uri ); + rc = opener->open ( xfer, uri ); + goto done; } } DBGC ( xfer, "XFER %p attempted to open unsupported URI scheme " "\"%s\"\n", xfer, uri->scheme ); - free_uri ( uri ); - return -ENOTSUP; + done: + uri_put ( uri ); + return rc; } /** diff --git a/src/core/uri.c b/src/core/uri.c index cb1ac3bc..6ebc6373 100644 --- a/src/core/uri.c +++ b/src/core/uri.c @@ -35,7 +35,7 @@ * * Splits a URI into its component parts. The return URI structure is * dynamically allocated and must eventually be freed by calling - * free_uri(). + * uri_put(). */ struct uri * parse_uri ( const char *uri_string ) { struct uri *uri; diff --git a/src/include/gpxe/open.h b/src/include/gpxe/open.h index b16bbe88..5e368486 100644 --- a/src/include/gpxe/open.h +++ b/src/include/gpxe/open.h @@ -46,9 +46,6 @@ struct uri_opener { * @v xfer Data transfer interface * @v uri URI * @ret rc Return status code - * - * This method takes ownership of the URI structure, and is - * responsible for eventually calling free_uri(). */ int ( * open ) ( struct xfer_interface *xfer, struct uri *uri ); }; diff --git a/src/include/gpxe/uri.h b/src/include/gpxe/uri.h index b8c7e098..6fddcc33 100644 --- a/src/include/gpxe/uri.h +++ b/src/include/gpxe/uri.h @@ -8,6 +8,7 @@ */ #include +#include /** A Uniform Resource Identifier * @@ -37,6 +38,8 @@ * query = "what=is", fragment = "this" */ struct uri { + /** Reference count */ + struct refcnt refcnt; /** Scheme */ const char *scheme; /** Opaque part */ @@ -100,15 +103,25 @@ static inline int uri_has_relative_path ( struct uri *uri ) { } /** - * Free URI structure + * Increment URI reference count * * @v uri URI - * - * Frees all the dynamically-allocated storage used by the URI - * structure. + * @ret uri URI */ -static inline void free_uri ( struct uri *uri ) { - free ( uri ); +static inline __attribute__ (( always_inline )) struct uri * +uri_get ( struct uri *uri ) { + ref_get ( &uri->refcnt ); + return uri; +} + +/** + * Decrement URI reference count + * + * @v uri URI + */ +static inline __attribute__ (( always_inline )) void +uri_put ( struct uri *uri ) { + ref_put ( &uri->refcnt ); } extern struct uri * parse_uri ( const char *uri_string ); From a6a18ae9af43f870c82836ec0c9b486db6e58b15 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 28 May 2007 20:09:44 +0000 Subject: [PATCH 08/15] Add xfer_[v]printf() functions. --- src/core/xfer.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/include/gpxe/xfer.h | 4 ++++ 2 files changed, 45 insertions(+) diff --git a/src/core/xfer.c b/src/core/xfer.c index 54377c70..2250687e 100644 --- a/src/core/xfer.c +++ b/src/core/xfer.c @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -221,6 +222,46 @@ int xfer_deliver_raw ( struct xfer_interface *xfer, return rc; } +/** + * Deliver formatted string + * + * @v xfer Data transfer interface + * @v format Format string + * @v args Arguments corresponding to the format string + * @ret rc Return status code + */ +int xfer_vprintf ( struct xfer_interface *xfer, const char *format, + va_list args ) { + size_t len; + va_list args_tmp; + + va_copy ( args_tmp, args ); + len = vsnprintf ( NULL, 0, format, args ); + { + char buf[len + 1]; + vsnprintf ( buf, sizeof ( buf ), format, args_tmp ); + return xfer_deliver_raw ( xfer, buf, len ); + } +} + +/** + * Deliver formatted string + * + * @v xfer Data transfer interface + * @v format Format string + * @v ... Arguments corresponding to the format string + * @ret rc Return status code + */ +int xfer_printf ( struct xfer_interface *xfer, const char *format, ... ) { + va_list args; + int rc; + + va_start ( args, format ); + rc = xfer_vprintf ( xfer, format, args ); + va_end ( args ); + return rc; +} + /**************************************************************************** * * Helper methods diff --git a/src/include/gpxe/xfer.h b/src/include/gpxe/xfer.h index e6c896c3..f946ab1c 100644 --- a/src/include/gpxe/xfer.h +++ b/src/include/gpxe/xfer.h @@ -143,6 +143,10 @@ extern int xfer_deliver_iob ( struct xfer_interface *xfer, struct io_buffer *iobuf ); extern int xfer_deliver_raw ( struct xfer_interface *xfer, const void *data, size_t len ); +extern int xfer_vprintf ( struct xfer_interface *xfer, + const char *format, va_list args ); +extern int xfer_printf ( struct xfer_interface *xfer, + const char *format, ... ); extern void ignore_xfer_close ( struct xfer_interface *xfer, int rc ); extern int ignore_xfer_vredirect ( struct xfer_interface *xfer, From 335b99a39d468c7671f584bee362856cf5c2ed99 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 31 May 2007 13:26:50 +0000 Subject: [PATCH 09/15] Move [v]ssnprintf() from iscsi.c into vsprintf.c; we need them elsewhere as well. --- src/core/vsprintf.c | 39 +++++++++++++++++++++++++++++++++++++ src/include/gpxe/vsprintf.h | 4 ++++ src/net/tcp/iscsi.c | 27 +------------------------ 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/core/vsprintf.c b/src/core/vsprintf.c index e6f072e7..4457fe4f 100644 --- a/src/core/vsprintf.c +++ b/src/core/vsprintf.c @@ -338,6 +338,45 @@ int snprintf ( char *buf, size_t size, const char *fmt, ... ) { return i; } +/** + * Version of vsnprintf() that accepts a signed buffer size + * + * @v buf Buffer into which to write the string + * @v size Size of buffer + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) { + + /* Treat negative buffer size as zero buffer size */ + if ( ssize < 0 ) + ssize = 0; + + /* Hand off to vsnprintf */ + return vsnprintf ( buf, ssize, fmt, args ); +} + +/** + * Version of vsnprintf() that accepts a signed buffer size + * + * @v buf Buffer into which to write the string + * @v size Size of buffer + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) { + va_list args; + int len; + + /* Hand off to vssnprintf */ + va_start ( args, fmt ); + len = vssnprintf ( buf, ssize, fmt, args ); + va_end ( args ); + return len; +} + /** * Write character to console * diff --git a/src/include/gpxe/vsprintf.h b/src/include/gpxe/vsprintf.h index ac87c5a8..9360f29b 100644 --- a/src/include/gpxe/vsprintf.h +++ b/src/include/gpxe/vsprintf.h @@ -64,4 +64,8 @@ struct printf_context { extern size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ); +extern int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, + va_list args ); +extern int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ); + #endif /* _GPXE_VSPRINTF_H */ diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index 56567976..f95286d0 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -348,32 +349,6 @@ static void iscsi_tx_data_out ( struct iscsi_session *iscsi, * */ -/** - * Version of snprintf() that accepts a signed buffer size - * - * @v buf Buffer into which to write the string - * @v size Size of buffer - * @v fmt Format string - * @v args Arguments corresponding to the format string - * @ret len Length of formatted string - * - * This is a utility function for iscsi_build_login_request_strings(). - */ -static int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) { - va_list args; - int len; - - /* Treat negative buffer size as zero buffer size */ - if ( ssize < 0 ) - ssize = 0; - - /* Hand off to vsnprintf */ - va_start ( args, fmt ); - len = vsnprintf ( buf, ssize, fmt, args ); - va_end ( args ); - return len; -} - /** * Build iSCSI login request strings * From 816c8f3b894e2cedf28e568329b6c69e45aa161e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 3 Jun 2007 01:50:03 +0000 Subject: [PATCH 10/15] Added [v]asprintf() --- src/core/asprintf.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ src/include/stdio.h | 5 +++++ 2 files changed, 52 insertions(+) create mode 100644 src/core/asprintf.c diff --git a/src/core/asprintf.c b/src/core/asprintf.c new file mode 100644 index 00000000..94d7e7c4 --- /dev/null +++ b/src/core/asprintf.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include + +/** + * Write a formatted string to newly allocated memory. + * + * @v strp Pointer to hold allocated string + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int vasprintf ( char **strp, const char *fmt, va_list args ) { + size_t len; + va_list args_tmp; + + /* Calculate length needed for string */ + va_copy ( args_tmp, args ); + len = ( vsnprintf ( NULL, 0, fmt, args_tmp ) + 1 ); + va_end ( args_tmp ); + + /* Allocate and fill string */ + *strp = malloc ( len ); + if ( ! *strp ) + return -ENOMEM; + return vsnprintf ( *strp, len, fmt, args ); +} + +/** + * Write a formatted string to newly allocated memory. + * + * @v strp Pointer to hold allocated string + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int asprintf ( char **strp, const char *fmt, ... ) { + va_list args; + int len; + + va_start ( args, fmt ); + len = vasprintf ( strp, fmt, args ); + va_end ( args ); + return len; +} diff --git a/src/include/stdio.h b/src/include/stdio.h index 169cfd9e..82077e20 100644 --- a/src/include/stdio.h +++ b/src/include/stdio.h @@ -10,10 +10,15 @@ printf ( const char *fmt, ... ); extern int __attribute__ (( format ( printf, 3, 4 ) )) snprintf ( char *buf, size_t size, const char *fmt, ... ); +extern int __attribute__ (( format ( printf, 2, 3 ) )) +asprintf ( char **strp, const char *fmt, ... ); + extern int vprintf ( const char *fmt, va_list args ); extern int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ); +extern int vasprintf ( char **strp, const char *fmt, va_list args ); + /** * Write a formatted string to a buffer * From 182e3ed61d8db574c2bf7a091ce2221746259cbd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 3 Jun 2007 01:51:10 +0000 Subject: [PATCH 11/15] Added dirname() --- src/core/basename.c | 22 ++++++++++++++++++++++ src/include/libgen.h | 3 ++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/core/basename.c b/src/core/basename.c index f50b7151..7340c0d5 100644 --- a/src/core/basename.c +++ b/src/core/basename.c @@ -38,3 +38,25 @@ char * basename ( char *path ) { basename = strrchr ( path, '/' ); return ( basename ? ( basename + 1 ) : path ); } + +/** + * Return directory name from path + * + * @v path Full path + * @ret dirname Directory name + * + * Note that this function may modify its argument. + */ +char * dirname ( char *path ) { + char *separator; + + separator = strrchr ( path, '/' ); + if ( separator == path ) { + return "/"; + } else if ( separator ) { + *separator = 0; + return path; + } else { + return "."; + } +} diff --git a/src/include/libgen.h b/src/include/libgen.h index 8fa552a9..56a2f760 100644 --- a/src/include/libgen.h +++ b/src/include/libgen.h @@ -1,6 +1,7 @@ #ifndef _LIBGEN_H #define _LIBGEN_H -char * basename ( char *path ); +extern char * basename ( char *path ); +extern char * dirname ( char *path ); #endif /* _LIBGEN_H */ From a6f0a098da98a4274b202300f62c5c4b871b3e92 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 3 Jun 2007 02:01:02 +0000 Subject: [PATCH 12/15] Add facility for resolving base+relative URIs (and paths). --- src/core/uri.c | 239 +++++++++++++++++++++++++++++++++++++---- src/include/gpxe/uri.h | 8 +- 2 files changed, 227 insertions(+), 20 deletions(-) diff --git a/src/core/uri.c b/src/core/uri.c index 6ebc6373..a793c457 100644 --- a/src/core/uri.c +++ b/src/core/uri.c @@ -25,8 +25,36 @@ #include #include #include +#include +#include #include +/** + * Dump URI for debugging + * + * @v uri URI + */ +static void dump_uri ( struct uri *uri ) { + if ( uri->scheme ) + DBG ( " scheme \"%s\"", uri->scheme ); + if ( uri->opaque ) + DBG ( " opaque \"%s\"", uri->opaque ); + if ( uri->user ) + DBG ( " user \"%s\"", uri->user ); + if ( uri->password ) + DBG ( " password \"%s\"", uri->password ); + if ( uri->host ) + DBG ( " host \"%s\"", uri->host ); + if ( uri->port ) + DBG ( " port \"%s\"", uri->port ); + if ( uri->path ) + DBG ( " path \"%s\"", uri->path ); + if ( uri->query ) + DBG ( " query \"%s\"", uri->query ); + if ( uri->fragment ) + DBG ( " fragment \"%s\"", uri->fragment ); +} + /** * Parse URI * @@ -136,25 +164,8 @@ struct uri * parse_uri ( const char *uri_string ) { } done: - DBG ( "URI \"%s\" split into", raw ); - if ( uri->scheme ) - DBG ( " scheme \"%s\"", uri->scheme ); - if ( uri->opaque ) - DBG ( " opaque \"%s\"", uri->opaque ); - if ( uri->user ) - DBG ( " user \"%s\"", uri->user ); - if ( uri->password ) - DBG ( " password \"%s\"", uri->password ); - if ( uri->host ) - DBG ( " host \"%s\"", uri->host ); - if ( uri->port ) - DBG ( " port \"%s\"", uri->port ); - if ( uri->path ) - DBG ( " path \"%s\"", uri->path ); - if ( uri->query ) - DBG ( " query \"%s\"", uri->query ); - if ( uri->fragment ) - DBG ( " fragment \"%s\"", uri->fragment ); + DBG ( "URI \"%s\" split into", uri_string ); + dump_uri ( uri ); DBG ( "\n" ); return uri; @@ -170,3 +181,193 @@ struct uri * parse_uri ( const char *uri_string ) { unsigned int uri_port ( struct uri *uri, unsigned int default_port ) { return ( uri->port ? strtoul ( uri->port, NULL, 0 ) : default_port ); } + +/** + * Unparse URI + * + * @v buf Buffer to fill with URI string + * @v size Size of buffer + * @v uri URI to write into buffer + * @ret len Length of URI string + */ +int unparse_uri ( char *buf, size_t size, struct uri *uri ) { + int used = 0; + + DBG ( "URI unparsing" ); + dump_uri ( uri ); + DBG ( "\n" ); + + /* Special-case opaque URIs */ + if ( uri->opaque ) { + return ssnprintf ( ( buf + used ), ( size - used ), + "%s:%s", uri->scheme, uri->opaque ); + } + + /* scheme:// */ + if ( uri->scheme ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "%s://", uri->scheme ); + } + + /* [user[:password]@]host[:port] */ + if ( uri->host ) { + if ( uri->user ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "%s", uri->user ); + if ( uri->password ) { + used += ssnprintf ( ( buf + used ), + ( size - used ), + ":%s", uri->password ); + } + used += ssnprintf ( ( buf + used ), ( size - used ), + "@" ); + } + used += ssnprintf ( ( buf + used ), ( size - used ), "%s", + uri->host ); + if ( uri->port ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + ":%s", uri->port ); + } + } + + /* /path */ + if ( uri->path ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "%s", uri->path ); + } + + /* ?query */ + if ( uri->query ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "?%s", uri->query ); + } + + /* #fragment */ + if ( uri->fragment ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "#%s", uri->fragment ); + } + + return used; +} + +/** + * Duplicate URI + * + * @v uri URI + * @ret uri Duplicate URI + * + * Creates a modifiable copy of a URI. + */ +struct uri * uri_dup ( struct uri *uri ) { + size_t len = ( unparse_uri ( NULL, 0, uri ) + 1 ); + char buf[len]; + + unparse_uri ( buf, len, uri ); + return parse_uri ( buf ); +} + +/** + * Resolve base+relative path + * + * @v base_uri Base path + * @v relative_uri Relative path + * @ret resolved_uri Resolved path + * + * Takes a base path (e.g. "/var/lib/tftpboot/vmlinuz" and a relative + * path (e.g. "initrd.gz") and produces a new path + * (e.g. "/var/lib/tftpboot/initrd.gz"). Note that any non-directory + * portion of the base path will automatically be stripped; this + * matches the semantics used when resolving the path component of + * URIs. + */ +char * resolve_path ( const char *base_path, + const char *relative_path ) { + size_t base_len = ( strlen ( base_path ) + 1 ); + char base_path_copy[base_len]; + char *base_tmp = base_path_copy; + char *resolved; + + /* If relative path is absolute, just re-use it */ + if ( relative_path[0] == '/' ) + return strdup ( relative_path ); + + /* Create modifiable copy of path for dirname() */ + memcpy ( base_tmp, base_path, base_len ); + base_tmp = dirname ( base_tmp ); + + /* Process "./" and "../" elements */ + while ( *relative_path == '.' ) { + relative_path++; + if ( *relative_path == 0 ) { + /* Do nothing */ + } else if ( *relative_path == '/' ) { + relative_path++; + } else if ( *relative_path == '.' ) { + relative_path++; + if ( *relative_path == 0 ) { + base_tmp = dirname ( base_tmp ); + } else if ( *relative_path == '/' ) { + base_tmp = dirname ( base_tmp ); + relative_path++; + } else { + relative_path -= 2; + break; + } + } else { + relative_path--; + break; + } + } + + /* Create and return new path */ + if ( asprintf ( &resolved, "%s%s%s", base_tmp, + ( ( base_tmp[ strlen ( base_tmp ) - 1 ] == '/' ) ? + "" : "/" ), relative_path ) < 0 ) + return NULL; + + return resolved; +} + +/** + * Resolve base+relative URI + * + * @v base_uri Base URI + * @v relative_uri Relative URI + * @ret resolved_uri Resolved URI + * + * Takes a base URI (e.g. "http://etherboot.org/kernels/vmlinuz" and a + * relative URI (e.g. "../initrds/initrd.gz") and produces a new URI + * (e.g. "http://etherboot.org/initrds/initrd.gz"). + */ +struct uri * resolve_uri ( struct uri *base_uri, + struct uri *relative_uri ) { + struct uri tmp_uri; + char *tmp_path = NULL; + struct uri *new_uri; + + /* If relative URI is absolute, just re-use it */ + if ( uri_is_absolute ( relative_uri ) ) + return uri_get ( relative_uri ); + + /* Mangle URI */ + memcpy ( &tmp_uri, base_uri, sizeof ( tmp_uri ) ); + if ( relative_uri->path ) { + tmp_path = resolve_path ( ( base_uri->path ? + base_uri->path : "/" ), + relative_uri->path ); + tmp_uri.path = tmp_path; + tmp_uri.query = relative_uri->query; + tmp_uri.fragment = relative_uri->fragment; + } else if ( relative_uri->query ) { + tmp_uri.query = relative_uri->query; + tmp_uri.fragment = relative_uri->fragment; + } else if ( relative_uri->fragment ) { + tmp_uri.fragment = relative_uri->fragment; + } + + /* Create demangled URI */ + new_uri = uri_dup ( &tmp_uri ); + free ( tmp_path ); + return new_uri; +} diff --git a/src/include/gpxe/uri.h b/src/include/gpxe/uri.h index 6fddcc33..f6162382 100644 --- a/src/include/gpxe/uri.h +++ b/src/include/gpxe/uri.h @@ -125,6 +125,12 @@ uri_put ( struct uri *uri ) { } extern struct uri * parse_uri ( const char *uri_string ); -unsigned int uri_port ( struct uri *uri, unsigned int default_port ); +extern unsigned int uri_port ( struct uri *uri, unsigned int default_port ); +extern int unparse_uri ( char *buf, size_t size, struct uri *uri ); +extern struct uri * uri_dup ( struct uri *uri ); +extern char * resolve_path ( const char *base_path, + const char *relative_path ); +extern struct uri * resolve_uri ( struct uri *base_uri, + struct uri *relative_uri ); #endif /* _GPXE_URI_H */ From 6a5cc3533fc3e133db49965184445c954d60eb65 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 3 Jun 2007 02:11:25 +0000 Subject: [PATCH 13/15] Added missing va_end() --- src/core/xfer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/xfer.c b/src/core/xfer.c index 2250687e..ea5fda3d 100644 --- a/src/core/xfer.c +++ b/src/core/xfer.c @@ -240,6 +240,7 @@ int xfer_vprintf ( struct xfer_interface *xfer, const char *format, { char buf[len + 1]; vsnprintf ( buf, sizeof ( buf ), format, args_tmp ); + va_end ( args_tmp ); return xfer_deliver_raw ( xfer, buf, len ); } } From b7ccdb7948b4909833eeb2c4ba7a51ad74ec2723 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 3 Jun 2007 02:15:00 +0000 Subject: [PATCH 14/15] Add a couple of tests for the URI parsing and resolving code. --- src/tests/uri_test.c | 145 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 src/tests/uri_test.c diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c new file mode 100644 index 00000000..25487603 --- /dev/null +++ b/src/tests/uri_test.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include + +#define URI_MAX_LEN 1024 + +struct uri_test { + const char *base_uri_string; + const char *relative_uri_string; + const char *resolved_uri_string; +}; + +static struct uri_test uri_tests[] = { + { "http://www.fensystems.co.uk", "", + "http://www.fensystems.co.uk/" }, + { "http://etherboot.org/wiki/page1", "page2", + "http://etherboot.org/wiki/page2" }, + { "http://etherboot.org/wiki/page1", "../page3", + "http://etherboot.org/page3" }, + { "tftp://192.168.0.1/", "/tftpboot/vmlinuz", + "tftp://192.168.0.1/tftpboot/vmlinuz" }, +#if 0 + "http://www.etherboot.org/wiki", + "mailto:bob@nowhere.com", + "ftp://joe:secret@insecure.org:8081/hidden/path/to?what=is#this", +#endif +}; + +static int test_parse_unparse ( const char *uri_string ) { + char buf[URI_MAX_LEN]; + size_t len; + struct uri *uri = NULL; + int rc; + + /* Parse and unparse URI */ + uri = parse_uri ( uri_string ); + if ( ! uri ) { + rc = -ENOMEM; + goto done; + } + len = unparse_uri ( buf, sizeof ( buf ), uri ); + + /* Compare result */ + if ( strcmp ( buf, uri_string ) != 0 ) { + printf ( "Unparse of \"%s\" produced \"%s\"\n", + uri_string, buf ); + rc = -EINVAL; + goto done; + } + + rc = 0; + + done: + uri_put ( uri ); + if ( rc ) { + printf ( "URI parse-unparse of \"%s\" failed: %s\n", + uri_string, strerror ( rc ) ); + } + return rc; +} + +static int test_resolve ( const char *base_uri_string, + const char *relative_uri_string, + const char *resolved_uri_string ) { + struct uri *base_uri = NULL; + struct uri *relative_uri = NULL; + struct uri *resolved_uri = NULL; + char buf[URI_MAX_LEN]; + size_t len; + int rc; + + /* Parse URIs */ + base_uri = parse_uri ( base_uri_string ); + if ( ! base_uri ) { + rc = -ENOMEM; + goto done; + } + relative_uri = parse_uri ( relative_uri_string ); + if ( ! relative_uri ) { + rc = -ENOMEM; + goto done; + } + + /* Resolve URI */ + resolved_uri = resolve_uri ( base_uri, relative_uri ); + if ( ! resolved_uri ) { + rc = -ENOMEM; + goto done; + } + + /* Compare result */ + len = unparse_uri ( buf, sizeof ( buf ), resolved_uri ); + if ( strcmp ( buf, resolved_uri_string ) != 0 ) { + printf ( "Resolution of \"%s\"+\"%s\" produced \"%s\"\n", + base_uri_string, relative_uri_string, buf ); + rc = -EINVAL; + goto done; + } + + rc = 0; + + done: + uri_put ( base_uri ); + uri_put ( relative_uri ); + uri_put ( resolved_uri ); + if ( rc ) { + printf ( "URI resolution of \"%s\"+\"%s\" failed: %s\n", + base_uri_string, relative_uri_string, + strerror ( rc ) ); + } + return rc; +} + +int uri_test ( void ) { + unsigned int i; + struct uri_test *uri_test; + int rc; + int overall_rc = 0; + + for ( i = 0 ; i < ( sizeof ( uri_tests ) / + sizeof ( uri_tests[0] ) ) ; i++ ) { + uri_test = &uri_tests[i]; + rc = test_parse_unparse ( uri_test->base_uri_string ); + if ( rc != 0 ) + overall_rc = rc; + rc = test_parse_unparse ( uri_test->relative_uri_string ); + if ( rc != 0 ) + overall_rc = rc; + rc = test_parse_unparse ( uri_test->resolved_uri_string ); + if ( rc != 0 ) + overall_rc = rc; + rc = test_resolve ( uri_test->base_uri_string, + uri_test->relative_uri_string, + uri_test->resolved_uri_string ); + if ( rc != 0 ) + overall_rc = rc; + } + + if ( overall_rc ) + printf ( "URI tests failed: %s\n", strerror ( overall_rc ) ); + return overall_rc; +} From ff84a66c357a9b7a8a03dafd9e2cd717566f8933 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 8 Jun 2007 12:37:35 +0000 Subject: [PATCH 15/15] Conflicts with native asn1.c --- src/crypto/axtls/asn1.c | 867 ---------------------------------------- 1 file changed, 867 deletions(-) delete mode 100644 src/crypto/axtls/asn1.c diff --git a/src/crypto/axtls/asn1.c b/src/crypto/axtls/asn1.c deleted file mode 100644 index 74411c70..00000000 --- a/src/crypto/axtls/asn1.c +++ /dev/null @@ -1,867 +0,0 @@ -/* - * Copyright(C) 2006 Cameron Rich - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/** - * @file asn1.c - * - * Some primitive asn methods for extraction rsa modulus information. It also - * is used for retrieving information from X.509 certificates. - */ - -#include -#include -#include -#include -#include "crypto.h" - -#define SIG_OID_PREFIX_SIZE 8 - -#define SIG_TYPE_MD2 0x02 -#define SIG_TYPE_MD5 0x04 -#define SIG_TYPE_SHA1 0x05 - -/* Must be an RSA algorithm with either SHA1 or MD5 for verifying to work */ -static const uint8_t sig_oid_prefix[SIG_OID_PREFIX_SIZE] = -{ - 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01 -}; - -/* CN, O, OU */ -static const uint8_t g_dn_types[] = { 3, 10, 11 }; - -static int get_asn1_length(const uint8_t *buf, int *offset) -{ - int len, i; - - if (!(buf[*offset] & 0x80)) /* short form */ - { - len = buf[(*offset)++]; - } - else /* long form */ - { - int length_bytes = buf[(*offset)++]&0x7f; - len = 0; - for (i = 0; i < length_bytes; i++) - { - len <<= 8; - len += buf[(*offset)++]; - } - } - - return len; -} - -/** - * Skip the ASN1.1 object type and its length. Get ready to read the object's - * data. - */ -int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type) -{ - if (buf[*offset] != obj_type) - return X509_NOT_OK; - (*offset)++; - return get_asn1_length(buf, offset); -} - -/** - * Skip over an ASN.1 object type completely. Get ready to read the next - * object. - */ -int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type) -{ - int len; - - if (buf[*offset] != obj_type) - return X509_NOT_OK; - (*offset)++; - len = get_asn1_length(buf, offset); - *offset += len; - return 0; -} - -/** - * Read an integer value for ASN.1 data - * Note: This function allocates memory which must be freed by the user. - */ -int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object) -{ - int len; - - if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0) - goto end_int_array; - - *object = (uint8_t *)malloc(len); - memcpy(*object, &buf[*offset], len); - *offset += len; - -end_int_array: - return len; -} - -#if 0 - -/** - * Get all the RSA private key specifics from an ASN.1 encoded file - */ -int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx) -{ - int offset = 7; - uint8_t *modulus, *priv_exp, *pub_exp; - int mod_len, priv_len, pub_len; -#ifdef CONFIG_BIGINT_CRT - uint8_t *p, *q, *dP, *dQ, *qInv; - int p_len, q_len, dP_len, dQ_len, qInv_len; -#endif - - /* not in der format */ - if (buf[0] != ASN1_SEQUENCE) /* basic sanity check */ - { -#ifdef CONFIG_SSL_FULL_MODE - printf("Error: This is not a valid ASN.1 file\n"); -#endif - return X509_INVALID_PRIV_KEY; - } - - /* initialise the RNG */ - RNG_initialize(buf, len); - - mod_len = asn1_get_int(buf, &offset, &modulus); - pub_len = asn1_get_int(buf, &offset, &pub_exp); - priv_len = asn1_get_int(buf, &offset, &priv_exp); - - if (mod_len <= 0 || pub_len <= 0 || priv_len <= 0) - return X509_INVALID_PRIV_KEY; - -#ifdef CONFIG_BIGINT_CRT - p_len = asn1_get_int(buf, &offset, &p); - q_len = asn1_get_int(buf, &offset, &q); - dP_len = asn1_get_int(buf, &offset, &dP); - dQ_len = asn1_get_int(buf, &offset, &dQ); - qInv_len = asn1_get_int(buf, &offset, &qInv); - - if (p_len <= 0 || q_len <= 0 || dP_len <= 0 || dQ_len <= 0 || qInv_len <= 0) - return X509_INVALID_PRIV_KEY; - - RSA_priv_key_new(rsa_ctx, - modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len, - p, p_len, q, p_len, dP, dP_len, dQ, dQ_len, qInv, qInv_len); - - free(p); - free(q); - free(dP); - free(dQ); - free(qInv); -#else - RSA_priv_key_new(rsa_ctx, - modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len); -#endif - - free(modulus); - free(priv_exp); - free(pub_exp); - return X509_OK; -} - -/** - * Get the time of a certificate. Ignore hours/minutes/seconds. - */ -static int asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t) -{ - int ret = X509_NOT_OK, len, t_offset; - struct tm tm; - - if (buf[(*offset)++] != ASN1_UTC_TIME) - goto end_utc_time; - len = get_asn1_length(buf, offset); - t_offset = *offset; - - memset(&tm, 0, sizeof(struct tm)); - tm.tm_year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0'); - - if (tm.tm_year <= 50) /* 1951-2050 thing */ - { - tm.tm_year += 100; - } - - tm.tm_mon = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0') - 1; - tm.tm_mday = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0'); - *t = mktime(&tm); - *offset += len; - ret = X509_OK; - -end_utc_time: - return ret; -} - -/** - * Get the version type of a certificate (which we don't actually care about) - */ -static int asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) -{ - int ret = X509_NOT_OK; - - (*offset) += 2; /* get past explicit tag */ - if (asn1_skip_obj(cert, offset, ASN1_INTEGER)) - goto end_version; - - ret = X509_OK; -end_version: - return ret; -} - -/** - * Retrieve the notbefore and notafter certificate times. - */ -static int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) -{ - return (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || - asn1_get_utc_time(cert, offset, &x509_ctx->not_before) || - asn1_get_utc_time(cert, offset, &x509_ctx->not_after)); -} - -/** - * Get the components of a distinguished name - */ -static int asn1_get_oid_x520(const uint8_t *buf, int *offset) -{ - int dn_type = 0; - int len; - - if ((len = asn1_next_obj(buf, offset, ASN1_OID)) < 0) - goto end_oid; - - /* expect a sequence of 2.5.4.[x] where x is a one of distinguished name - components we are interested in. */ - if (len == 3 && buf[(*offset)++] == 0x55 && buf[(*offset)++] == 0x04) - dn_type = buf[(*offset)++]; - else - { - *offset += len; /* skip over it */ - } - -end_oid: - return dn_type; -} - -/** - * Obtain an ASN.1 printable string type. - */ -static int asn1_get_printable_str(const uint8_t *buf, int *offset, char **str) -{ - int len = X509_NOT_OK; - - /* some certs have this awful crud in them for some reason */ - if (buf[*offset] != ASN1_PRINTABLE_STR && - buf[*offset] != ASN1_TELETEX_STR && buf[*offset] != ASN1_IA5_STR) - goto end_pnt_str; - - (*offset)++; - len = get_asn1_length(buf, offset); - *str = (char *)malloc(len+1); /* allow for null */ - memcpy(*str, &buf[*offset], len); - (*str)[len] = 0; /* null terminate */ - *offset += len; -end_pnt_str: - return len; -} - -/** - * Get the subject name (or the issuer) of a certificate. - */ -static int asn1_name(const uint8_t *cert, int *offset, char *dn[]) -{ - int ret = X509_NOT_OK; - int dn_type; - char *tmp = NULL; - - if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0) - goto end_name; - - while (asn1_next_obj(cert, offset, ASN1_SET) >= 0) - { - int i, found = 0; - - if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || - (dn_type = asn1_get_oid_x520(cert, offset)) < 0) - goto end_name; - - if (asn1_get_printable_str(cert, offset, &tmp) < 0) - { - free(tmp); - goto end_name; - } - - /* find the distinguished named type */ - for (i = 0; i < X509_NUM_DN_TYPES; i++) - { - if (dn_type == g_dn_types[i]) - { - if (dn[i] == NULL) - { - dn[i] = tmp; - found = 1; - break; - } - } - } - - if (found == 0) /* not found so get rid of it */ - { - free(tmp); - } - } - - ret = X509_OK; -end_name: - return ret; -} - -/** - * Read the modulus and public exponent of a certificate. - */ -static int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) -{ - int ret = X509_NOT_OK, mod_len, pub_len; - uint8_t *modulus, *pub_exp; - - if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || - asn1_skip_obj(cert, offset, ASN1_SEQUENCE) || - asn1_next_obj(cert, offset, ASN1_BIT_STRING) < 0) - goto end_pub_key; - - (*offset)++; - - if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0) - goto end_pub_key; - - mod_len = asn1_get_int(cert, offset, &modulus); - pub_len = asn1_get_int(cert, offset, &pub_exp); - - RSA_pub_key_new(&x509_ctx->rsa_ctx, modulus, mod_len, pub_exp, pub_len); - - free(modulus); - free(pub_exp); - ret = X509_OK; - -end_pub_key: - return ret; -} - -#ifdef CONFIG_SSL_CERT_VERIFICATION -/** - * Read the signature of the certificate. - */ -static int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) -{ - int ret = X509_NOT_OK; - - if (cert[(*offset)++] != ASN1_BIT_STRING) - goto end_sig; - - x509_ctx->sig_len = get_asn1_length(cert, offset); - x509_ctx->signature = (uint8_t *)malloc(x509_ctx->sig_len); - memcpy(x509_ctx->signature, &cert[*offset], x509_ctx->sig_len); - *offset += x509_ctx->sig_len; - ret = X509_OK; - -end_sig: - return ret; -} - -/* - * Compare 2 distinguished name components for equality - * @return 0 if a match - */ -static int asn1_compare_dn_comp(const char *dn1, const char *dn2) -{ - int ret = 1; - - if ((dn1 && dn2 == NULL) || (dn1 == NULL && dn2)) goto err_no_match; - - ret = (dn1 && dn2) ? strcmp(dn1, dn2) : 0; - -err_no_match: - return ret; -} - -/** - * Clean up all of the CA certificates. - */ -void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx) -{ - int i = 0; - - while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) - { - x509_free(ca_cert_ctx->cert[i]); - ca_cert_ctx->cert[i++] = NULL; - } - - free(ca_cert_ctx); -} - -/* - * Compare 2 distinguished names for equality - * @return 0 if a match - */ -static int asn1_compare_dn(char * const dn1[], char * const dn2[]) -{ - int i; - - for (i = 0; i < X509_NUM_DN_TYPES; i++) - { - if (asn1_compare_dn_comp(dn1[i], dn2[i])) - { - return 1; - } - } - - return 0; /* all good */ -} - -/** - * Retrieve the signature from a certificate. - */ -const uint8_t *x509_get_signature(const uint8_t *asn1_sig, int *len) -{ - int offset = 0; - const uint8_t *ptr = NULL; - - if (asn1_next_obj(asn1_sig, &offset, ASN1_SEQUENCE) < 0 || - asn1_skip_obj(asn1_sig, &offset, ASN1_SEQUENCE)) - goto end_get_sig; - - if (asn1_sig[offset++] != ASN1_OCTET_STRING) - goto end_get_sig; - *len = get_asn1_length(asn1_sig, &offset); - ptr = &asn1_sig[offset]; /* all ok */ - -end_get_sig: - return ptr; -} - -#endif - -/** - * Read the signature type of the certificate. We only support RSA-MD5 and - * RSA-SHA1 signature types. - */ -static int asn1_signature_type(const uint8_t *cert, - int *offset, X509_CTX *x509_ctx) -{ - int ret = X509_NOT_OK, len; - - if (cert[(*offset)++] != ASN1_OID) - goto end_check_sig; - - len = get_asn1_length(cert, offset); - - if (memcmp(sig_oid_prefix, &cert[*offset], SIG_OID_PREFIX_SIZE)) - goto end_check_sig; /* unrecognised cert type */ - - x509_ctx->sig_type = cert[*offset + SIG_OID_PREFIX_SIZE]; - - *offset += len; - if (asn1_skip_obj(cert, offset, ASN1_NULL)) - goto end_check_sig; - ret = X509_OK; - -end_check_sig: - return ret; -} - -/** - * Construct a new x509 object. - * @return 0 if ok. < 0 if there was a problem. - */ -int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx) -{ - int begin_tbs, end_tbs; - int ret = X509_NOT_OK, offset = 0, cert_size = 0; - X509_CTX *x509_ctx; - BI_CTX *bi_ctx; - - *ctx = (X509_CTX *)calloc(1, sizeof(X509_CTX)); - x509_ctx = *ctx; - - /* get the certificate size */ - asn1_skip_obj(cert, &cert_size, ASN1_SEQUENCE); - - if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) - goto end_cert; - - begin_tbs = offset; /* start of the tbs */ - end_tbs = begin_tbs; /* work out the end of the tbs */ - asn1_skip_obj(cert, &end_tbs, ASN1_SEQUENCE); - - if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) - goto end_cert; - - if (cert[offset] == ASN1_EXPLICIT_TAG) /* optional version */ - { - if (asn1_version(cert, &offset, x509_ctx)) - goto end_cert; - } - - if (asn1_skip_obj(cert, &offset, ASN1_INTEGER) || /* serial number */ - asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) - goto end_cert; - - /* make sure the signature is ok */ - if (asn1_signature_type(cert, &offset, x509_ctx)) - { - ret = X509_VFY_ERROR_UNSUPPORTED_DIGEST; - goto end_cert; - } - - if (asn1_name(cert, &offset, x509_ctx->ca_cert_dn) || - asn1_validity(cert, &offset, x509_ctx) || - asn1_name(cert, &offset, x509_ctx->cert_dn) || - asn1_public_key(cert, &offset, x509_ctx)) - goto end_cert; - - bi_ctx = x509_ctx->rsa_ctx->bi_ctx; - -#ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */ - /* use the appropriate signature algorithm (either SHA1 or MD5) */ - if (x509_ctx->sig_type == SIG_TYPE_MD5) - { - MD5_CTX md5_ctx; - uint8_t md5_dgst[MD5_SIZE]; - MD5Init(&md5_ctx); - MD5Update(&md5_ctx, &cert[begin_tbs], end_tbs-begin_tbs); - MD5Final(&md5_ctx, md5_dgst); - x509_ctx->digest = bi_import(bi_ctx, md5_dgst, MD5_SIZE); - } - else if (x509_ctx->sig_type == SIG_TYPE_SHA1) - { - SHA1_CTX sha_ctx; - uint8_t sha_dgst[SHA1_SIZE]; - SHA1Init(&sha_ctx); - SHA1Update(&sha_ctx, &cert[begin_tbs], end_tbs-begin_tbs); - SHA1Final(&sha_ctx, sha_dgst); - x509_ctx->digest = bi_import(bi_ctx, sha_dgst, SHA1_SIZE); - } - - offset = end_tbs; /* skip the v3 data */ - if (asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) || - asn1_signature(cert, &offset, x509_ctx)) - goto end_cert; -#endif - - if (len) - { - *len = cert_size; - } - - ret = X509_OK; -end_cert: - -#ifdef CONFIG_SSL_FULL_MODE - if (ret) - { - printf("Error: Invalid X509 ASN.1 file\n"); - } -#endif - - return ret; -} - -/** - * Free an X.509 object's resources. - */ -void x509_free(X509_CTX *x509_ctx) -{ - X509_CTX *next; - int i; - - if (x509_ctx == NULL) /* if already null, then don't bother */ - return; - - for (i = 0; i < X509_NUM_DN_TYPES; i++) - { - free(x509_ctx->ca_cert_dn[i]); - free(x509_ctx->cert_dn[i]); - } - - free(x509_ctx->signature); - -#ifdef CONFIG_SSL_CERT_VERIFICATION - if (x509_ctx->digest) - { - bi_free(x509_ctx->rsa_ctx->bi_ctx, x509_ctx->digest); - } -#endif - - RSA_free(x509_ctx->rsa_ctx); - - next = x509_ctx->next; - free(x509_ctx); - x509_free(next); /* clear the chain */ -} - -#ifdef CONFIG_SSL_CERT_VERIFICATION -/** - * Do some basic checks on the certificate chain. - * - * Certificate verification consists of a number of checks: - * - A root certificate exists in the certificate store. - * - The date of the certificate is after the start date. - * - The date of the certificate is before the finish date. - * - The certificate chain is valid. - * - That the certificate(s) are not self-signed. - * - The signature of the certificate is valid. - */ -int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) -{ - int ret = X509_OK, i = 0; - bigint *cert_sig; - X509_CTX *next_cert = NULL; - BI_CTX *ctx; - bigint *mod, *expn; - struct timeval tv; - int match_ca_cert = 0; - - if (cert == NULL || ca_cert_ctx == NULL) - { - ret = X509_VFY_ERROR_NO_TRUSTED_CERT; - goto end_verify; - } - - /* last cert in the chain - look for a trusted cert */ - if (cert->next == NULL) - { - while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) - { - if (asn1_compare_dn(cert->ca_cert_dn, - ca_cert_ctx->cert[i]->cert_dn) == 0) - { - match_ca_cert = 1; - break; - } - - i++; - } - - if (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) - { - next_cert = ca_cert_ctx->cert[i]; - } - else /* trusted cert not found */ - { - ret = X509_VFY_ERROR_NO_TRUSTED_CERT; - goto end_verify; - } - } - else - { - next_cert = cert->next; - } - - gettimeofday(&tv, NULL); - - /* check the not before date */ - if (tv.tv_sec < cert->not_before) - { - ret = X509_VFY_ERROR_NOT_YET_VALID; - goto end_verify; - } - - /* check the not after date */ - if (tv.tv_sec > cert->not_after) - { - ret = X509_VFY_ERROR_EXPIRED; - goto end_verify; - } - - /* check the chain integrity */ - if (asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn)) - { - ret = X509_VFY_ERROR_INVALID_CHAIN; - goto end_verify; - } - - /* check for self-signing */ - if (!match_ca_cert && asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0) - { - ret = X509_VFY_ERROR_SELF_SIGNED; - goto end_verify; - } - - /* check the signature */ - ctx = cert->rsa_ctx->bi_ctx; - mod = next_cert->rsa_ctx->m; - expn = next_cert->rsa_ctx->e; - cert_sig = RSA_sign_verify(ctx, cert->signature, cert->sig_len, - bi_clone(ctx, mod), bi_clone(ctx, expn)); - - if (cert_sig) - { - ret = cert->digest ? /* check the signature */ - bi_compare(cert_sig, cert->digest) : - X509_VFY_ERROR_UNSUPPORTED_DIGEST; - bi_free(ctx, cert_sig); - - if (ret) - goto end_verify; - } - else - { - ret = X509_VFY_ERROR_BAD_SIGNATURE; - goto end_verify; - } - - /* go down the certificate chain using recursion. */ - if (ret == 0 && cert->next) - { - ret = x509_verify(ca_cert_ctx, next_cert); - } - -end_verify: - return ret; -} -#endif - -#if defined (CONFIG_SSL_FULL_MODE) -/** - * Used for diagnostics. - */ -void x509_print(CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) -{ - if (cert == NULL) - return; - - printf("---------------- CERT DEBUG ----------------\n"); - printf("* CA Cert Distinguished Name\n"); - if (cert->ca_cert_dn[X509_COMMON_NAME]) - { - printf("Common Name (CN):\t%s\n", cert->ca_cert_dn[X509_COMMON_NAME]); - } - - if (cert->ca_cert_dn[X509_ORGANIZATION]) - { - printf("Organization (O):\t%s\n", cert->ca_cert_dn[X509_ORGANIZATION]); - } - - if (cert->ca_cert_dn[X509_ORGANIZATIONAL_TYPE]) - { - printf("Organizational Unit (OU): %s\n", - cert->ca_cert_dn[X509_ORGANIZATIONAL_TYPE]); - } - - printf("* Cert Distinguished Name\n"); - if (cert->cert_dn[X509_COMMON_NAME]) - { - printf("Common Name (CN):\t%s\n", cert->cert_dn[X509_COMMON_NAME]); - } - - if (cert->cert_dn[X509_ORGANIZATION]) - { - printf("Organization (O):\t%s\n", cert->cert_dn[X509_ORGANIZATION]); - } - - if (cert->cert_dn[X509_ORGANIZATIONAL_TYPE]) - { - printf("Organizational Unit (OU): %s\n", - cert->cert_dn[X509_ORGANIZATIONAL_TYPE]); - } - - printf("Not Before:\t\t%s", ctime(&cert->not_before)); - printf("Not After:\t\t%s", ctime(&cert->not_after)); - printf("RSA bitsize:\t\t%d\n", cert->rsa_ctx->num_octets*8); - printf("Sig Type:\t\t"); - switch (cert->sig_type) - { - case SIG_TYPE_MD5: - printf("MD5\n"); - break; - case SIG_TYPE_SHA1: - printf("SHA1\n"); - break; - case SIG_TYPE_MD2: - printf("MD2\n"); - break; - default: - printf("Unrecognized: %d\n", cert->sig_type); - break; - } - - printf("Verify:\t\t\t"); - - if (ca_cert_ctx) - { - x509_display_error(x509_verify(ca_cert_ctx, cert)); - } - - printf("\n"); -#if 0 - print_blob("Signature", cert->signature, cert->sig_len); - bi_print("Modulus", cert->rsa_ctx->m); - bi_print("Pub Exp", cert->rsa_ctx->e); -#endif - - if (ca_cert_ctx) - { - x509_print(ca_cert_ctx, cert->next); - } -} - -void x509_display_error(int error) -{ - switch (error) - { - case X509_NOT_OK: - printf("X509 not ok"); - break; - - case X509_VFY_ERROR_NO_TRUSTED_CERT: - printf("No trusted cert is available"); - break; - - case X509_VFY_ERROR_BAD_SIGNATURE: - printf("Bad signature"); - break; - - case X509_VFY_ERROR_NOT_YET_VALID: - printf("Cert is not yet valid"); - break; - - case X509_VFY_ERROR_EXPIRED: - printf("Cert has expired"); - break; - - case X509_VFY_ERROR_SELF_SIGNED: - printf("Cert is self-signed"); - break; - - case X509_VFY_ERROR_INVALID_CHAIN: - printf("Chain is invalid (check order of certs)"); - break; - - case X509_VFY_ERROR_UNSUPPORTED_DIGEST: - printf("Unsupported digest"); - break; - - case X509_INVALID_PRIV_KEY: - printf("Invalid private key"); - break; - } -} -#endif /* CONFIG_SSL_FULL_MODE */ - -#endif