mirror of https://github.com/ipxe/ipxe.git
Merge branch 'master' into mcb-tcp-xfer
commit
6f0a6c09db
|
@ -0,0 +1,47 @@
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -38,3 +38,25 @@ char * basename ( char *path ) {
|
||||||
basename = strrchr ( path, '/' );
|
basename = strrchr ( path, '/' );
|
||||||
return ( basename ? ( basename + 1 ) : 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 ".";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ int start_download ( const char *uri_string, struct async *parent,
|
||||||
err:
|
err:
|
||||||
async_uninit ( &download->async );
|
async_uninit ( &download->async );
|
||||||
ufree ( download->buffer.addr );
|
ufree ( download->buffer.addr );
|
||||||
free_uri ( download->uri );
|
uri_put ( download->uri );
|
||||||
free ( download );
|
free ( download );
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ static void download_sigchld ( struct async *async,
|
||||||
/* Discard the buffer */
|
/* Discard the buffer */
|
||||||
ufree ( download->buffer.addr );
|
ufree ( download->buffer.addr );
|
||||||
}
|
}
|
||||||
free_uri ( download->uri );
|
uri_put ( download->uri );
|
||||||
download->uri = NULL;
|
download->uri = NULL;
|
||||||
|
|
||||||
/* Terminate ourselves */
|
/* Terminate ourselves */
|
||||||
|
|
|
@ -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
|
* 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 */
|
/** Downloader job control interface operations */
|
||||||
static struct job_interface_operations downloader_job_operations = {
|
static struct job_interface_operations downloader_job_operations = {
|
||||||
.start = downloader_job_start,
|
.start = ignore_job_start,
|
||||||
.done = ignore_job_done,
|
.done = ignore_job_done,
|
||||||
.kill = downloader_job_kill,
|
.kill = downloader_job_kill,
|
||||||
.progress = ignore_job_progress,
|
.progress = ignore_job_progress,
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <gpxe/refcnt.h>
|
#include <gpxe/refcnt.h>
|
||||||
|
#include <gpxe/process.h>
|
||||||
#include <gpxe/xfer.h>
|
#include <gpxe/xfer.h>
|
||||||
#include <gpxe/open.h>
|
#include <gpxe/open.h>
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@
|
||||||
struct hw {
|
struct hw {
|
||||||
struct refcnt refcnt;
|
struct refcnt refcnt;
|
||||||
struct xfer_interface xfer;
|
struct xfer_interface xfer;
|
||||||
|
struct process process;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char hw_msg[] = "Hello world!\n";
|
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 ) {
|
static void hw_finished ( struct hw *hw, int rc ) {
|
||||||
xfer_nullify ( &hw->xfer );
|
xfer_nullify ( &hw->xfer );
|
||||||
xfer_close ( &hw->xfer, rc );
|
xfer_close ( &hw->xfer, rc );
|
||||||
|
process_del ( &hw->process );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hw_xfer_close ( struct xfer_interface *xfer, int rc ) {
|
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 );
|
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 = {
|
static struct xfer_interface_operations hw_xfer_operations = {
|
||||||
.close = hw_xfer_close,
|
.close = hw_xfer_close,
|
||||||
.vredirect = ignore_xfer_vredirect,
|
.vredirect = ignore_xfer_vredirect,
|
||||||
.request = hw_xfer_request,
|
.request = ignore_xfer_request,
|
||||||
.seek = ignore_xfer_seek,
|
.seek = ignore_xfer_seek,
|
||||||
.deliver_iob = xfer_deliver_as_raw,
|
.deliver_iob = xfer_deliver_as_raw,
|
||||||
.deliver_raw = ignore_xfer_deliver_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 ) {
|
static int hw_open ( struct xfer_interface *xfer, struct uri *uri __unused ) {
|
||||||
struct hw *hw;
|
struct hw *hw;
|
||||||
|
|
||||||
|
@ -59,6 +61,7 @@ static int hw_open ( struct xfer_interface *xfer, struct uri *uri __unused ) {
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
memset ( hw, 0, sizeof ( *hw ) );
|
memset ( hw, 0, sizeof ( *hw ) );
|
||||||
xfer_init ( &hw->xfer, &hw_xfer_operations, &hw->refcnt );
|
xfer_init ( &hw->xfer, &hw_xfer_operations, &hw->refcnt );
|
||||||
|
process_init ( &hw->process, hw_step, &hw->refcnt );
|
||||||
|
|
||||||
/* Attach parent interface, mortalise self, and return */
|
/* Attach parent interface, mortalise self, and return */
|
||||||
xfer_plug_plug ( &hw->xfer, xfer );
|
xfer_plug_plug ( &hw->xfer, xfer );
|
||||||
|
|
|
@ -52,6 +52,7 @@ static struct socket_opener socket_openers_end[0]
|
||||||
int xfer_open_uri ( struct xfer_interface *xfer, const char *uri_string ) {
|
int xfer_open_uri ( struct xfer_interface *xfer, const char *uri_string ) {
|
||||||
struct uri *uri;
|
struct uri *uri;
|
||||||
struct uri_opener *opener;
|
struct uri_opener *opener;
|
||||||
|
int rc = -ENOTSUP;
|
||||||
|
|
||||||
DBGC ( xfer, "XFER %p opening URI %s\n", xfer, uri_string );
|
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++ ) {
|
for ( opener = uri_openers ; opener < uri_openers_end ; opener++ ) {
|
||||||
if ( strcmp ( uri->scheme, opener->scheme ) == 0 ) {
|
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 "
|
DBGC ( xfer, "XFER %p attempted to open unsupported URI scheme "
|
||||||
"\"%s\"\n", xfer, uri->scheme );
|
"\"%s\"\n", xfer, uri->scheme );
|
||||||
free_uri ( uri );
|
done:
|
||||||
return -ENOTSUP;
|
uri_put ( uri );
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open socket
|
* Open socket
|
||||||
*
|
*
|
||||||
* @v xfer Data transfer interface
|
* @v xfer Data transfer interface
|
||||||
* @v domain Communication domain (e.g. PF_INET)
|
* @v semantics Communication semantics (e.g. SOCK_STREAM)
|
||||||
* @v type Communication semantics (e.g. SOCK_STREAM)
|
|
||||||
* @v peer Peer socket address
|
* @v peer Peer socket address
|
||||||
* @v local Local socket address, or NULL
|
* @v local Local socket address, or NULL
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
int xfer_open_socket ( struct xfer_interface *xfer,
|
int xfer_open_socket ( struct xfer_interface *xfer, int semantics,
|
||||||
int domain, int type, struct sockaddr *peer,
|
struct sockaddr *peer, struct sockaddr *local ) {
|
||||||
struct sockaddr *local ) {
|
|
||||||
struct socket_opener *opener;
|
struct socket_opener *opener;
|
||||||
|
|
||||||
DBGC ( xfer, "XFER %p opening (%s,%s) socket\n", xfer,
|
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++ ){
|
for ( opener = socket_openers; opener < socket_openers_end; opener++ ){
|
||||||
if ( ( opener->domain == domain ) &&
|
if ( ( opener->semantics == semantics ) &&
|
||||||
( opener->type == type ) ) {
|
( opener->family == peer->sa_family ) ) {
|
||||||
return opener->open ( xfer, peer, local );
|
return opener->open ( xfer, peer, local );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DBGC ( xfer, "XFER %p attempted to open unsupported socket type "
|
DBGC ( xfer, "XFER %p attempted to open unsupported socket type "
|
||||||
"(%s,%s)\n", xfer, socket_domain_name ( domain ),
|
"(%s,%s)\n", xfer, socket_semantics_name ( semantics ),
|
||||||
socket_type_name ( type ) );
|
socket_family_name ( peer->sa_family ) );
|
||||||
return -ENOTSUP;
|
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 ); }
|
return xfer_open_uri ( xfer, uri_string ); }
|
||||||
case LOCATION_SOCKET: {
|
case LOCATION_SOCKET: {
|
||||||
int domain = va_arg ( args, int );
|
int semantics = va_arg ( args, int );
|
||||||
int type = va_arg ( args, int );
|
|
||||||
struct sockaddr *peer = va_arg ( args, struct sockaddr * );
|
struct sockaddr *peer = va_arg ( args, struct sockaddr * );
|
||||||
struct sockaddr *local = 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:
|
default:
|
||||||
DBGC ( xfer, "XFER %p attempted to open unsupported location "
|
DBGC ( xfer, "XFER %p attempted to open unsupported location "
|
||||||
"type %d\n", xfer, type );
|
"type %d\n", xfer, type );
|
||||||
|
|
|
@ -78,6 +78,7 @@ static void posix_file_free ( struct refcnt *refcnt ) {
|
||||||
struct io_buffer *tmp;
|
struct io_buffer *tmp;
|
||||||
|
|
||||||
list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) {
|
list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) {
|
||||||
|
list_del ( &iobuf->list );
|
||||||
free_iob ( iobuf );
|
free_iob ( iobuf );
|
||||||
}
|
}
|
||||||
free ( file );
|
free ( file );
|
||||||
|
@ -190,6 +191,7 @@ static int posix_find_free_fd ( void ) {
|
||||||
if ( ! posix_fd_to_file ( fd ) )
|
if ( ! posix_fd_to_file ( fd ) )
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
DBG ( "POSIX could not find free file descriptor\n" );
|
||||||
return -ENFILE;
|
return -ENFILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,13 +227,11 @@ int open ( const char *uri_string ) {
|
||||||
if ( ( rc = xfer_open_uri ( &file->xfer, uri_string ) ) != 0 )
|
if ( ( rc = xfer_open_uri ( &file->xfer, uri_string ) ) != 0 )
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* Request data */
|
|
||||||
if ( ( rc = xfer_request_all ( &file->xfer ) ) != 0 )
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
/* Wait for open to succeed or fail */
|
/* Wait for open to succeed or fail */
|
||||||
while ( list_empty ( &file->data ) ) {
|
while ( list_empty ( &file->data ) ) {
|
||||||
step();
|
step();
|
||||||
|
if ( file->rc == 0 )
|
||||||
|
break;
|
||||||
if ( file->rc != -EINPROGRESS ) {
|
if ( file->rc != -EINPROGRESS ) {
|
||||||
rc = file->rc;
|
rc = file->rc;
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -240,6 +240,7 @@ int open ( const char *uri_string ) {
|
||||||
|
|
||||||
/* Add to list of open files. List takes reference ownership. */
|
/* Add to list of open files. List takes reference ownership. */
|
||||||
list_add ( &file->list, &posix_files );
|
list_add ( &file->list, &posix_files );
|
||||||
|
DBG ( "POSIX opened %s as file %d\n", uri_string, fd );
|
||||||
return fd;
|
return fd;
|
||||||
|
|
||||||
err:
|
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,
|
copy_to_user ( buffer, offset, iobuf->data,
|
||||||
frag_len );
|
frag_len );
|
||||||
iob_pull ( iobuf, frag_len );
|
iob_pull ( iobuf, frag_len );
|
||||||
if ( ! iob_len ( iobuf ) )
|
if ( ! iob_len ( iobuf ) ) {
|
||||||
|
list_del ( &iobuf-> list );
|
||||||
free_iob ( iobuf );
|
free_iob ( iobuf );
|
||||||
|
}
|
||||||
file->pos += frag_len;
|
file->pos += frag_len;
|
||||||
len += frag_len;
|
len += frag_len;
|
||||||
offset += frag_len;
|
offset += frag_len;
|
||||||
|
|
|
@ -31,25 +31,43 @@
|
||||||
static LIST_HEAD ( run_queue );
|
static LIST_HEAD ( run_queue );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add process to run queue
|
* Add process to process list
|
||||||
*
|
*
|
||||||
* @v process Process
|
* @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 );
|
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
|
* Single-step a single process
|
||||||
*
|
*
|
||||||
* This removes the first process from the run queue and executes a
|
* This executes a single step of the first process in the run queue,
|
||||||
* single step of that process.
|
* and moves the process to the end of the run queue.
|
||||||
*/
|
*/
|
||||||
void step ( void ) {
|
void step ( void ) {
|
||||||
struct process *process;
|
struct process *process;
|
||||||
|
|
||||||
list_for_each_entry ( process, &run_queue, list ) {
|
list_for_each_entry ( process, &run_queue, list ) {
|
||||||
list_del ( &process->list );
|
list_del ( &process->list );
|
||||||
|
list_add_tail ( &process->list, &run_queue );
|
||||||
process->step ( process );
|
process->step ( process );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,8 @@ void ref_get ( struct refcnt *refcnt ) {
|
||||||
|
|
||||||
refcnt->refcnt++;
|
refcnt->refcnt++;
|
||||||
|
|
||||||
DBGC ( refcnt, "REFCNT %p incremented to %d\n",
|
DBGC2 ( refcnt, "REFCNT %p incremented to %d\n",
|
||||||
refcnt, refcnt->refcnt );
|
refcnt, refcnt->refcnt );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,8 +59,8 @@ void ref_put ( struct refcnt *refcnt ) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
refcnt->refcnt--;
|
refcnt->refcnt--;
|
||||||
DBGC ( refcnt, "REFCNT %p decremented to %d\n",
|
DBGC2 ( refcnt, "REFCNT %p decremented to %d\n",
|
||||||
refcnt, refcnt->refcnt );
|
refcnt, refcnt->refcnt );
|
||||||
|
|
||||||
if ( refcnt->refcnt >= 0 )
|
if ( refcnt->refcnt >= 0 )
|
||||||
return;
|
return;
|
||||||
|
|
241
src/core/uri.c
241
src/core/uri.c
|
@ -25,8 +25,36 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <gpxe/vsprintf.h>
|
||||||
#include <gpxe/uri.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
|
* Parse URI
|
||||||
*
|
*
|
||||||
|
@ -35,7 +63,7 @@
|
||||||
*
|
*
|
||||||
* Splits a URI into its component parts. The return URI structure is
|
* Splits a URI into its component parts. The return URI structure is
|
||||||
* dynamically allocated and must eventually be freed by calling
|
* dynamically allocated and must eventually be freed by calling
|
||||||
* free_uri().
|
* uri_put().
|
||||||
*/
|
*/
|
||||||
struct uri * parse_uri ( const char *uri_string ) {
|
struct uri * parse_uri ( const char *uri_string ) {
|
||||||
struct uri *uri;
|
struct uri *uri;
|
||||||
|
@ -136,25 +164,8 @@ struct uri * parse_uri ( const char *uri_string ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
DBG ( "URI \"%s\" split into", raw );
|
DBG ( "URI \"%s\" split into", uri_string );
|
||||||
if ( uri->scheme )
|
dump_uri ( uri );
|
||||||
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 ( "\n" );
|
DBG ( "\n" );
|
||||||
|
|
||||||
return uri;
|
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 ) {
|
unsigned int uri_port ( struct uri *uri, unsigned int default_port ) {
|
||||||
return ( uri->port ? strtoul ( uri->port, NULL, 0 ) : 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;
|
||||||
|
}
|
||||||
|
|
|
@ -338,6 +338,45 @@ int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
|
||||||
return i;
|
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
|
* Write character to console
|
||||||
*
|
*
|
||||||
|
|
113
src/core/xfer.c
113
src/core/xfer.c
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <gpxe/xfer.h>
|
#include <gpxe/xfer.h>
|
||||||
|
|
||||||
|
@ -35,6 +36,8 @@
|
||||||
void xfer_close ( struct xfer_interface *xfer, int rc ) {
|
void xfer_close ( struct xfer_interface *xfer, int rc ) {
|
||||||
struct xfer_interface *dest = xfer_get_dest ( xfer );
|
struct xfer_interface *dest = xfer_get_dest ( xfer );
|
||||||
|
|
||||||
|
DBGC ( xfer, "XFER %p->%p close\n", xfer, dest );
|
||||||
|
|
||||||
dest->op->close ( dest, rc );
|
dest->op->close ( dest, rc );
|
||||||
xfer_unplug ( xfer );
|
xfer_unplug ( xfer );
|
||||||
xfer_put ( dest );
|
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 );
|
struct xfer_interface *dest = xfer_get_dest ( xfer );
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
DBGC ( xfer, "XFER %p->%p redirect\n", xfer, dest );
|
||||||
|
|
||||||
rc = dest->op->vredirect ( dest, type, args );
|
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 );
|
xfer_put ( dest );
|
||||||
return rc;
|
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 );
|
struct xfer_interface *dest = xfer_get_dest ( xfer );
|
||||||
int rc;
|
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 );
|
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 );
|
xfer_put ( dest );
|
||||||
return rc;
|
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
|
* 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 );
|
struct xfer_interface *dest = xfer_get_dest ( xfer );
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
DBGC ( xfer, "XFER %p->%p seek %s+%ld\n", xfer, dest,
|
||||||
|
whence_text ( whence ), offset );
|
||||||
|
|
||||||
rc = dest->op->seek ( dest, offset, whence );
|
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 );
|
xfer_put ( dest );
|
||||||
return rc;
|
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
|
* 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 xfer_interface *dest = xfer_get_dest ( xfer );
|
||||||
struct io_buffer *iobuf;
|
struct io_buffer *iobuf;
|
||||||
|
|
||||||
|
DBGC ( xfer, "XFER %p->%p alloc_iob %zd\n", xfer, dest, len );
|
||||||
|
|
||||||
iobuf = dest->op->alloc_iob ( 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 );
|
xfer_put ( dest );
|
||||||
return iobuf;
|
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 );
|
struct xfer_interface *dest = xfer_get_dest ( xfer );
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
DBGC ( xfer, "XFER %p->%p deliver_iob %zd\n", xfer, dest,
|
||||||
|
iob_len ( iobuf ) );
|
||||||
|
|
||||||
rc = dest->op->deliver_iob ( dest, 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 );
|
xfer_put ( dest );
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -165,11 +209,60 @@ int xfer_deliver_raw ( struct xfer_interface *xfer,
|
||||||
struct xfer_interface *dest = xfer_get_dest ( xfer );
|
struct xfer_interface *dest = xfer_get_dest ( xfer );
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
DBGC ( xfer, "XFER %p->%p deliver_raw %p+%zd\n", xfer, dest,
|
||||||
|
data, len );
|
||||||
|
|
||||||
rc = dest->op->deliver_raw ( 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 );
|
xfer_put ( dest );
|
||||||
return rc;
|
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
|
* Helper methods
|
||||||
|
|
|
@ -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 <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
|
|
|
@ -120,4 +120,5 @@ struct errortab common_errors[] __errortab = {
|
||||||
{ ENOENT, "File not found" },
|
{ ENOENT, "File not found" },
|
||||||
{ ENETUNREACH, "Network unreachable" },
|
{ ENETUNREACH, "Network unreachable" },
|
||||||
{ ETIMEDOUT, "Connection timed out" },
|
{ ETIMEDOUT, "Connection timed out" },
|
||||||
|
{ EPIPE, "Broken pipe" },
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,7 +26,9 @@ enum {
|
||||||
*
|
*
|
||||||
* Parameter list for open() is:
|
* Parameter list for open() is:
|
||||||
*
|
*
|
||||||
*
|
* int semantics;
|
||||||
|
* struct sockaddr *peer;
|
||||||
|
* struct sockaddr *local;
|
||||||
*/
|
*/
|
||||||
LOCATION_SOCKET,
|
LOCATION_SOCKET,
|
||||||
};
|
};
|
||||||
|
@ -44,9 +46,6 @@ struct uri_opener {
|
||||||
* @v xfer Data transfer interface
|
* @v xfer Data transfer interface
|
||||||
* @v uri URI
|
* @v uri URI
|
||||||
* @ret rc Return status code
|
* @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 );
|
int ( * open ) ( struct xfer_interface *xfer, struct uri *uri );
|
||||||
};
|
};
|
||||||
|
@ -56,10 +55,10 @@ struct uri_opener {
|
||||||
|
|
||||||
/** A socket opener */
|
/** A socket opener */
|
||||||
struct socket_opener {
|
struct socket_opener {
|
||||||
/** Communication domain (e.g. PF_INET) */
|
|
||||||
int domain;
|
|
||||||
/** Communication semantics (e.g. SOCK_STREAM) */
|
/** Communication semantics (e.g. SOCK_STREAM) */
|
||||||
int type;
|
int semantics;
|
||||||
|
/** Address family (e.g. AF_INET) */
|
||||||
|
int family;
|
||||||
/** Open socket
|
/** Open socket
|
||||||
*
|
*
|
||||||
* @v xfer Data transfer interface
|
* @v xfer Data transfer interface
|
||||||
|
@ -76,9 +75,11 @@ struct socket_opener {
|
||||||
|
|
||||||
extern int xfer_open_uri ( struct xfer_interface *xfer,
|
extern int xfer_open_uri ( struct xfer_interface *xfer,
|
||||||
const char *uri_string );
|
const char *uri_string );
|
||||||
extern int xfer_open_socket ( struct xfer_interface *xfer,
|
extern int xfer_open_named_socket ( struct xfer_interface *xfer,
|
||||||
int domain, int type, struct sockaddr *peer,
|
int semantics, struct sockaddr *peer,
|
||||||
struct sockaddr *local );
|
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_vopen ( struct xfer_interface *xfer, int type, va_list args );
|
||||||
extern int xfer_open ( struct xfer_interface *xfer, int type, ... );
|
extern int xfer_open ( struct xfer_interface *xfer, int type, ... );
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gpxe/list.h>
|
#include <gpxe/list.h>
|
||||||
|
#include <gpxe/refcnt.h>
|
||||||
|
|
||||||
/** A process */
|
/** A process */
|
||||||
struct process {
|
struct process {
|
||||||
|
@ -19,14 +20,46 @@ struct process {
|
||||||
* This method should execute a single step of the process.
|
* This method should execute a single step of the process.
|
||||||
* Returning from this method is isomorphic to yielding the
|
* Returning from this method is isomorphic to yielding the
|
||||||
* CPU to another process.
|
* 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 );
|
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 );
|
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 */
|
#endif /* _GPXE_PROCESS_H */
|
||||||
|
|
|
@ -37,4 +37,15 @@ struct retry_timer {
|
||||||
extern void start_timer ( struct retry_timer *timer );
|
extern void start_timer ( struct retry_timer *timer );
|
||||||
extern void stop_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 */
|
#endif /* _GPXE_RETRY_H */
|
||||||
|
|
|
@ -8,31 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @defgroup commdomains Communication domains
|
* @defgroup commtypes Communication semantics
|
||||||
*
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
#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
|
|
||||||
*
|
*
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
|
@ -41,14 +17,14 @@ socket_domain_name ( int domain ) {
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name communication type
|
* Name communication semantics
|
||||||
*
|
*
|
||||||
* @v type Communication type (e.g. SOCK_STREAM)
|
* @v semantics Communication semantics (e.g. SOCK_STREAM)
|
||||||
* @ret name Name of communication type
|
* @ret name Name of communication semantics
|
||||||
*/
|
*/
|
||||||
static inline __attribute__ (( always_inline )) const char *
|
static inline __attribute__ (( always_inline )) const char *
|
||||||
socket_type_name ( int type ) {
|
socket_semantics_name ( int semantics ) {
|
||||||
switch ( type ) {
|
switch ( semantics ) {
|
||||||
case SOCK_STREAM: return "SOCK_STREAM";
|
case SOCK_STREAM: return "SOCK_STREAM";
|
||||||
case SOCK_DGRAM: return "SOCK_DGRAM";
|
case SOCK_DGRAM: return "SOCK_DGRAM";
|
||||||
default: return "SOCK_UNKNOWN";
|
default: return "SOCK_UNKNOWN";
|
||||||
|
@ -64,6 +40,21 @@ socket_type_name ( int type ) {
|
||||||
#define AF_INET6 2 /**< IPv6 Internet addresses */
|
#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 */
|
/** A socket address family */
|
||||||
typedef uint16_t sa_family_t;
|
typedef uint16_t sa_family_t;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <gpxe/refcnt.h>
|
||||||
|
|
||||||
/** A Uniform Resource Identifier
|
/** A Uniform Resource Identifier
|
||||||
*
|
*
|
||||||
|
@ -37,6 +38,8 @@
|
||||||
* query = "what=is", fragment = "this"
|
* query = "what=is", fragment = "this"
|
||||||
*/
|
*/
|
||||||
struct uri {
|
struct uri {
|
||||||
|
/** Reference count */
|
||||||
|
struct refcnt refcnt;
|
||||||
/** Scheme */
|
/** Scheme */
|
||||||
const char *scheme;
|
const char *scheme;
|
||||||
/** Opaque part */
|
/** 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
|
* @v uri URI
|
||||||
*
|
* @ret uri URI
|
||||||
* Frees all the dynamically-allocated storage used by the URI
|
|
||||||
* structure.
|
|
||||||
*/
|
*/
|
||||||
static inline void free_uri ( struct uri *uri ) {
|
static inline __attribute__ (( always_inline )) struct uri *
|
||||||
free ( 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 );
|
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 */
|
#endif /* _GPXE_URI_H */
|
||||||
|
|
|
@ -64,4 +64,8 @@ struct printf_context {
|
||||||
|
|
||||||
extern size_t vcprintf ( struct printf_context *ctx, const char *fmt,
|
extern size_t vcprintf ( struct printf_context *ctx, const char *fmt,
|
||||||
va_list args );
|
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 */
|
#endif /* _GPXE_VSPRINTF_H */
|
||||||
|
|
|
@ -112,6 +112,20 @@ enum seek_whence {
|
||||||
SEEK_CUR,
|
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 null_xfer;
|
||||||
extern struct xfer_interface_operations null_xfer_ops;
|
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_redirect ( struct xfer_interface *xfer, int type, ... );
|
||||||
extern int xfer_request ( struct xfer_interface *xfer, off_t offset,
|
extern int xfer_request ( struct xfer_interface *xfer, off_t offset,
|
||||||
int whence, size_t len );
|
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_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,
|
extern struct io_buffer * xfer_alloc_iob ( struct xfer_interface *xfer,
|
||||||
size_t len );
|
size_t len );
|
||||||
extern int xfer_deliver_iob ( struct xfer_interface *xfer,
|
extern int xfer_deliver_iob ( struct xfer_interface *xfer,
|
||||||
struct io_buffer *iobuf );
|
struct io_buffer *iobuf );
|
||||||
extern int xfer_deliver_raw ( struct xfer_interface *xfer,
|
extern int xfer_deliver_raw ( struct xfer_interface *xfer,
|
||||||
const void *data, size_t len );
|
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 void ignore_xfer_close ( struct xfer_interface *xfer, int rc );
|
||||||
extern int ignore_xfer_vredirect ( struct xfer_interface *xfer,
|
extern int ignore_xfer_vredirect ( struct xfer_interface *xfer,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef _LIBGEN_H
|
#ifndef _LIBGEN_H
|
||||||
#define _LIBGEN_H
|
#define _LIBGEN_H
|
||||||
|
|
||||||
char * basename ( char *path );
|
extern char * basename ( char *path );
|
||||||
|
extern char * dirname ( char *path );
|
||||||
|
|
||||||
#endif /* _LIBGEN_H */
|
#endif /* _LIBGEN_H */
|
||||||
|
|
|
@ -10,10 +10,15 @@ printf ( const char *fmt, ... );
|
||||||
extern int __attribute__ (( format ( printf, 3, 4 ) ))
|
extern int __attribute__ (( format ( printf, 3, 4 ) ))
|
||||||
snprintf ( char *buf, size_t size, const char *fmt, ... );
|
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 vprintf ( const char *fmt, va_list args );
|
||||||
|
|
||||||
extern int vsnprintf ( char *buf, size_t size, 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
|
* Write a formatted string to a buffer
|
||||||
*
|
*
|
||||||
|
|
|
@ -388,7 +388,7 @@ int net_rx ( struct io_buffer *iobuf, struct net_device *netdev,
|
||||||
* This polls all interfaces for received packets, and processes
|
* This polls all interfaces for received packets, and processes
|
||||||
* packets from the RX queue.
|
* 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 net_device *netdev;
|
||||||
struct io_buffer *iobuf;
|
struct io_buffer *iobuf;
|
||||||
|
|
||||||
|
@ -410,9 +410,6 @@ static void net_step ( struct process *process ) {
|
||||||
netdev->ll_protocol->rx ( iobuf, netdev );
|
netdev->ll_protocol->rx ( iobuf, netdev );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Re-schedule ourself */
|
|
||||||
schedule ( process );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Networking stack process */
|
/** Networking stack process */
|
||||||
|
@ -422,7 +419,7 @@ static struct process net_process = {
|
||||||
|
|
||||||
/** Initialise the networking stack process */
|
/** Initialise the networking stack process */
|
||||||
static void init_net ( void ) {
|
static void init_net ( void ) {
|
||||||
schedule ( &net_process );
|
process_add ( &net_process );
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_FN ( INIT_PROCESS, init_net, NULL, NULL );
|
INIT_FN ( INIT_PROCESS, init_net, NULL, NULL );
|
||||||
|
|
|
@ -64,7 +64,7 @@ static LIST_HEAD ( timers );
|
||||||
* be stopped and the timer's callback function will be called.
|
* be stopped and the timer's callback function will be called.
|
||||||
*/
|
*/
|
||||||
void start_timer ( struct retry_timer *timer ) {
|
void start_timer ( struct retry_timer *timer ) {
|
||||||
if ( ! timer->start )
|
if ( ! timer_running ( timer ) )
|
||||||
list_add ( &timer->list, &timers );
|
list_add ( &timer->list, &timers );
|
||||||
timer->start = currticks();
|
timer->start = currticks();
|
||||||
if ( timer->timeout < MIN_TIMEOUT )
|
if ( timer->timeout < MIN_TIMEOUT )
|
||||||
|
@ -86,7 +86,7 @@ void stop_timer ( struct retry_timer *timer ) {
|
||||||
unsigned long runtime;
|
unsigned long runtime;
|
||||||
|
|
||||||
/* If timer was already stopped, do nothing */
|
/* If timer was already stopped, do nothing */
|
||||||
if ( ! timer->start )
|
if ( ! timer_running ( timer ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
list_del ( &timer->list );
|
list_del ( &timer->list );
|
||||||
|
@ -153,7 +153,7 @@ static void timer_expired ( struct retry_timer *timer ) {
|
||||||
*
|
*
|
||||||
* @v process Retry timer process
|
* @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 *timer;
|
||||||
struct retry_timer *tmp;
|
struct retry_timer *tmp;
|
||||||
unsigned long now = currticks();
|
unsigned long now = currticks();
|
||||||
|
@ -164,8 +164,6 @@ static void retry_step ( struct process *process ) {
|
||||||
if ( used >= timer->timeout )
|
if ( used >= timer->timeout )
|
||||||
timer_expired ( timer );
|
timer_expired ( timer );
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule ( process );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Retry timer process */
|
/** Retry timer process */
|
||||||
|
@ -175,7 +173,7 @@ static struct process retry_process = {
|
||||||
|
|
||||||
/** Initialise the retry timer module */
|
/** Initialise the retry timer module */
|
||||||
static void init_retry ( void ) {
|
static void init_retry ( void ) {
|
||||||
schedule ( &retry_process );
|
process_add ( &retry_process );
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_FN ( INIT_PROCESS, init_retry, NULL, NULL );
|
INIT_FN ( INIT_PROCESS, init_retry, NULL, NULL );
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <byteswap.h>
|
#include <byteswap.h>
|
||||||
|
#include <gpxe/vsprintf.h>
|
||||||
#include <gpxe/scsi.h>
|
#include <gpxe/scsi.h>
|
||||||
#include <gpxe/process.h>
|
#include <gpxe/process.h>
|
||||||
#include <gpxe/uaccess.h>
|
#include <gpxe/uaccess.h>
|
||||||
|
@ -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
|
* Build iSCSI login request strings
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
#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;
|
||||||
|
}
|
Loading…
Reference in New Issue