--- /dev/null
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+/**
+ * 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;
+}
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 ".";
+ }
+}
err:
async_uninit ( &download->async );
ufree ( download->buffer.addr );
- free_uri ( download->uri );
+ uri_put ( download->uri );
free ( download );
return rc;
}
/* Discard the buffer */
ufree ( download->buffer.addr );
}
- free_uri ( download->uri );
+ uri_put ( download->uri );
download->uri = NULL;
/* Terminate ourselves */
*
*/
-/**
- * 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
*
/** 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,
#include <string.h>
#include <errno.h>
#include <gpxe/refcnt.h>
+#include <gpxe/process.h>
#include <gpxe/xfer.h>
#include <gpxe/open.h>
struct hw {
struct refcnt refcnt;
struct xfer_interface xfer;
+ struct process process;
};
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 ) {
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;
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 );
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 );
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;
}
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 );
struct io_buffer *tmp;
list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) {
+ list_del ( &iobuf->list );
free_iob ( iobuf );
}
free ( file );
if ( ! posix_fd_to_file ( fd ) )
return fd;
}
+ DBG ( "POSIX could not find free file descriptor\n" );
return -ENFILE;
}
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;
/* 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:
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;
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;
}
refcnt->refcnt++;
- DBGC ( refcnt, "REFCNT %p incremented to %d\n",
- refcnt, refcnt->refcnt );
+ DBGC2 ( refcnt, "REFCNT %p incremented to %d\n",
+ refcnt, 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;
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <libgen.h>
+#include <gpxe/vsprintf.h>
#include <gpxe/uri.h>
+/**
+ * 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
*
*
* 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;
}
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;
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;
+}
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
*
*/
#include <string.h>
+#include <stdio.h>
#include <errno.h>
#include <gpxe/xfer.h>
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 );
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;
}
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
*
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
*
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;
}
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;
}
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
+++ /dev/null
-/*
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#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
{ ENOENT, "File not found" },
{ ENETUNREACH, "Network unreachable" },
{ ETIMEDOUT, "Connection timed out" },
+ { EPIPE, "Broken pipe" },
};
*
* Parameter list for open() is:
*
- *
+ * int semantics;
+ * struct sockaddr *peer;
+ * struct sockaddr *local;
*/
LOCATION_SOCKET,
};
* @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 );
};
/** 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
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, ... );
*/
#include <gpxe/list.h>
+#include <gpxe/refcnt.h>
/** A process */
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 */
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 */
*/
/**
- * @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
*
* @{
*/
/** @} */
/**
- * 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";
#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;
*/
#include <stdlib.h>
+#include <gpxe/refcnt.h>
/** A Uniform Resource Identifier
*
* query = "what=is", fragment = "this"
*/
struct uri {
+ /** Reference count */
+ struct refcnt refcnt;
/** Scheme */
const char *scheme;
/** Opaque part */
}
/**
- * Free URI structure
+ * Increment URI reference count
*
* @v uri URI
+ * @ret uri URI
+ */
+static inline __attribute__ (( always_inline )) struct uri *
+uri_get ( struct uri *uri ) {
+ ref_get ( &uri->refcnt );
+ return uri;
+}
+
+/**
+ * Decrement URI reference count
*
- * Frees all the dynamically-allocated storage used by the URI
- * structure.
+ * @v uri URI
*/
-static inline void free_uri ( struct uri *uri ) {
- free ( 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 */
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 */
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;
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,
#ifndef _LIBGEN_H
#define _LIBGEN_H
-char * basename ( char *path );
+extern char * basename ( char *path );
+extern char * dirname ( char *path );
#endif /* _LIBGEN_H */
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
*
* 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;
netdev->ll_protocol->rx ( iobuf, netdev );
}
}
-
- /* Re-schedule ourself */
- schedule ( process );
}
/** Networking stack 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 );
* 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 )
unsigned long runtime;
/* If timer was already stopped, do nothing */
- if ( ! timer->start )
+ if ( ! timer_running ( timer ) )
return;
list_del ( &timer->list );
*
* @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();
if ( used >= timer->timeout )
timer_expired ( timer );
}
-
- schedule ( process );
}
/** Retry timer 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 );
#include <errno.h>
#include <assert.h>
#include <byteswap.h>
+#include <gpxe/vsprintf.h>
#include <gpxe/scsi.h>
#include <gpxe/process.h>
#include <gpxe/uaccess.h>
*
*/
-/**
- * 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
*
--- /dev/null
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/uri.h>
+
+#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;
+}