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/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/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/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/open.c b/src/core/open.c index 284d00a9..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,44 +62,45 @@ 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; } /** * 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 +119,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/core/posix_io.c b/src/core/posix_io.c index b98766f8..3b5660e4 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 ); @@ -190,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; } @@ -225,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; @@ -240,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: @@ -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; 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/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; diff --git a/src/core/uri.c b/src/core/uri.c index cb1ac3bc..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 * @@ -35,7 +63,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; @@ -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/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/core/xfer.c b/src/core/xfer.c index f2783f5b..ea5fda3d 100644 --- a/src/core/xfer.c +++ b/src/core/xfer.c @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -35,6 +36,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 +55,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 +99,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 +124,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 +162,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 +184,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,11 +209,60 @@ 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; } +/** + * 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 ); + va_end ( 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/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 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" }, }; diff --git a/src/include/gpxe/open.h b/src/include/gpxe/open.h index 229d2d78..5e368486 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, }; @@ -44,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 ); }; @@ -56,10 +55,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 +75,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/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/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; diff --git a/src/include/gpxe/uri.h b/src/include/gpxe/uri.h index b8c7e098..f6162382 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,18 +103,34 @@ 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 ); -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 */ 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/include/gpxe/xfer.h b/src/include/gpxe/xfer.h index 71b69dc5..f946ab1c 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,14 +135,18 @@ 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, 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, 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 */ 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 * 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 ); 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 * 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; +}