diff --git a/hostname.c b/hostname.c index 91bc278..22f0fc4 100644 --- a/hostname.c +++ b/hostname.c @@ -7,7 +7,14 @@ Boolean DoLookupName(Session *sess) { dnrBuffer dnrInfo; - TCPIPDNRNameToIP(sess->domainName, &dnrInfo); + if (TCPIPValidateIPString(sess->hostName)) { + cvtRec cvtInfo; + TCPIPConvertIPToHex(&cvtInfo, sess->hostName); + sess->ipAddr = cvtInfo.cvtIPAddress; + return TRUE; + } + + TCPIPDNRNameToIP(sess->hostName, &dnrInfo); if (toolerror()) return FALSE; @@ -22,6 +29,9 @@ Boolean DoLookupName(Session *sess) { sess->ipAddr = dnrInfo.DNRIPaddress; return TRUE; } else { + if (dnrInfo.DNRstatus == DNR_Pending) { + TCPIPCancelDNR(&dnrInfo); + } return FALSE; } } diff --git a/http.c b/http.c index e4470a6..165a4c7 100644 --- a/http.c +++ b/http.c @@ -7,14 +7,14 @@ #include "session.h" #include "http.h" -Boolean BuildHTTPRequest(Session *sess, char *resourceStr, char *hostStr) { +Boolean BuildHTTPRequest(Session *sess, char *resourceStr) { int sizeNeeded = 0; int rangeOffset; int round = 0; do { sizeNeeded = snprintf(sess->httpRequest, sizeNeeded, - "GET %s HTTP/1.1\r\n" + "GET /%s HTTP/1.1\r\n" "Host: %s\r\n" "User-Agent: GSRemoteDisk/0.1\r\n" "Accept-Encoding: identity\r\n" @@ -23,7 +23,7 @@ Boolean BuildHTTPRequest(Session *sess, char *resourceStr, char *hostStr) { "Range: bytes=%n1234567890-1234567890\r\n" "\r\n", resourceStr, - hostStr, + sess->hostName+1, &rangeOffset); if (sizeNeeded <= 0) { diff --git a/http.h b/http.h index 25a949c..5a6af25 100644 --- a/http.h +++ b/http.h @@ -4,7 +4,7 @@ #include #include "session.h" -Boolean BuildHTTPRequest(Session *sess, char *resourceStr, char *hostStr); +Boolean BuildHTTPRequest(Session *sess, char *resourceStr); void UpdateRequestRange(Session *sess, unsigned long start, unsigned long end); #endif \ No newline at end of file diff --git a/main.c b/main.c index 27a7b0d..00b63d7 100644 --- a/main.c +++ b/main.c @@ -5,8 +5,10 @@ #include #include #include +#include #include "http.h" #include "session.h" +#include "seturl.h" #include "hostname.h" #define buffTypePointer 0x0000 /* For TCPIPReadTCP() */ @@ -21,38 +23,48 @@ http://ia800505.us.archive.org/16/items/a2gs_System_1.0_1986_Apple_FW/System_1.0 static char delimitStr[] = "\p\r\n\r\n"; -int main(void) { +char *defaultURL = "http://ia800505.us.archive.org/16/items/a2gs_System_1.0_1986_Apple_FW/System_1.0_1986_Apple_FW.2mg"; + + +int main(int argc, char **argv) { TLStartUp(); LoadOneTool(54,0x200); TCPIPStartUp(); - TCPIPConnect(NULL); + if (TCPIPGetConnectStatus() == FALSE) { + TCPIPConnect(NULL); + if (toolerror()) + goto exit; + } - Session sess; - FILE *f = fopen("out", "wb"); + Session sess = {0}; + + char *url = (argc > 1) ? argv[1] : defaultURL; - BuildHTTPRequest(&sess, "/16/items/a2gs_System_1.0_1986_Apple_FW/System_1.0_1986_Apple_FW.2mg", "ia800505.us.archive.org"); - fputs(sess.httpRequest, f); - - UpdateRequestRange(&sess, 0, 511); - fputs(sess.httpRequest, f); - - strcpy(sess.domainName, "\pia800505.us.archive.org"); - Boolean result = DoLookupName(&sess); - - if (result) { - printf("IP = %lu.%lu.%lu.%lu\n", - sess.ipAddr & 0xFF, - (sess.ipAddr >> 8) & 0xFF, - (sess.ipAddr >> 16) & 0xFF, - (sess.ipAddr >> 24)); - } else { - printf("Address lookup failed\n"); + enum SetURLResult result = SetURL(&sess, url, TRUE, FALSE); + + if (result != SETURL_SUCCESSFUL) { + printf("SetURL error %i\n", (int)result); goto exit; } - sess.port = 80; + unsigned long startByte = 0; + if (argc > 2) { + startByte = strtoul(argv[2], NULL, 0); + } + UpdateRequestRange(&sess, startByte, startByte + 511); + + printf("Request:\n"); + printf("=========\n"); + int i; + for (i = 0; sess.httpRequest[i] != '\0'; i++) { + char ch = sess.httpRequest[i]; + if (ch != '\r') + putchar(ch); + } + printf("=========\n"); + printf("\n", i); int err; if ((err = StartTCPConnection(&sess)) != 0) { @@ -61,18 +73,20 @@ int main(void) { } TCPIPWriteTCP(sess.ipid, sess.httpRequest, strlen(sess.httpRequest), TRUE, FALSE); - - LongWord startTime = GetTick(); - - while (GetTick() - startTime < 3*60) { - //printf("polling\n"); - TCPIPPoll(); - } - rlrBuff rlrBuff; - TCPIPReadLineTCP(sess.ipid, (void*)((LongWord)delimitStr | 0x80000000), - buffTypeNewHandle, (Ref)NULL, - 0xFFFFFF, &rlrBuff); + rlrBuff rlrBuff = {0}; + Word tcpError; + do { + TCPIPPoll(); + tcpError = TCPIPReadLineTCP(sess.ipid, + (void*)((LongWord)delimitStr | 0x80000000), + buffTypeNewHandle, (Ref)NULL, + 0xFFFFFF, &rlrBuff); + if (tcpError || toolerror()) { + printf("tcpError = %u, toolerror = %u\n", tcpError, toolerror()); + break; + } + } while (rlrBuff.rlrBuffCount == 0); printf("Response:\n"); printf("=========\n"); @@ -81,11 +95,8 @@ int main(void) { if (ch != '\r') putchar(ch); } - //printf("%s\n", *(rlrBuff.rlrBuffHandle)); printf("=========\n"); - - printf("Response headers handle size = %lu \n", GetHandleSize(rlrBuff.rlrBuffHandle)); - printf("Reported response size = %lu \n", rlrBuff.rlrBuffCount); + printf("Response size = %lu\n", rlrBuff.rlrBuffCount); EndTCPConnection(&sess); diff --git a/session.h b/session.h index 8787697..c219352 100644 --- a/session.h +++ b/session.h @@ -25,8 +25,8 @@ typedef struct Session { LongWord ipAddr; Word port; - /* Domain name of host (p-string; empty string for host specified by IP) */ - char domainName[256]; + /* Domain name or IP address of host (p-string, but also null-terminated) */ + char hostName[257]; /* Time (GetTick) of last DNS lookup */ LongWord dnsTime; } Session; diff --git a/seturl.c b/seturl.c new file mode 100644 index 0000000..62aff0c --- /dev/null +++ b/seturl.c @@ -0,0 +1,95 @@ +#include +#include +#include "session.h" +#include "urlparser.h" +#include "strcasecmp.h" +#include "hostname.h" +#include "http.h" +#include "tcpconnection.h" +#include "seturl.h" + +#define DEFAULT_HTTP_PORT 80 + +/* Limit to make sure sizes stay within the range of 16-bit values */ +#define MAX_URL_LENGTH 30000 + + +enum SetURLResult +SetURL(Session *sess, char *url, Boolean permissive, Boolean partialOK) { + if (strlen(url) > MAX_URL_LENGTH) { + return URL_TOO_LONG; + } + + for(unsigned int i = 0; url[i] != '\0'; i++) { + if (url[i] <= ' ' && url[i] >= 0) { + return INVALID_CHARACTER_IN_URL; + } + } + + URLParts urlParts = ParseURL(url); + + if (urlParts.errorFound) + return BAD_URL_SYNTAX; + + if (urlParts.scheme == NULL) { + if (permissive) { + urlParts.scheme = "http"; + } else { + return BAD_URL_SYNTAX; + } + } else if (strcasecmp(urlParts.scheme, "http") != 0) { + return UNSUPPORTED_URL_SCHEME; + } + + if (urlParts.username != NULL || urlParts.password != NULL) { + return AUTHENTICATION_NOT_SUPPORTED; + } + + if (urlParts.fragment != NULL) { + return FRAGMENT_NOT_SUPPORTED; + } + + unsigned long portNum; + char *endPtr; + if (urlParts.port == NULL || *urlParts.port == '\0') { + portNum = DEFAULT_HTTP_PORT; + } else { + errno = 0; + portNum = strtoul(urlParts.port, &endPtr, 10); + if (errno || *endPtr != '\0' || portNum > 0xFFFF) { + return INVALID_PORT_NUMBER; + } + } + sess->port = portNum; + + if (urlParts.host == NULL) { + if (!partialOK || sess->hostName[0] == 0) { + return NO_HOST_SPECIFIED; + } + } else if (*urlParts.host == '\0') { + return NO_HOST_SPECIFIED; + } else if (*urlParts.host == '[') { + return IPV6_NOT_SUPPORTED; + } else { + size_t len; + if ((len = strlen(urlParts.host)) > 255) { + return HOSTNAME_TOO_LONG; + } + + strcpy(&sess->hostName[1], urlParts.host); + sess->hostName[0] = len; + + if (!DoLookupName(sess)) { + return NAME_LOOKUP_FAILED; + } + } + + if (!BuildHTTPRequest(sess, urlParts.path)) { + return OUT_OF_MEMORY; + } + + /* End any existing TCP connection to old URL */ + EndTCPConnection(sess); + + return SETURL_SUCCESSFUL; +} diff --git a/seturl.h b/seturl.h new file mode 100644 index 0000000..652428e --- /dev/null +++ b/seturl.h @@ -0,0 +1,23 @@ +#ifndef SETURL_H +#define SETURL_H + +enum SetURLResult { + SETURL_SUCCESSFUL = 0, + URL_TOO_LONG, + INVALID_CHARACTER_IN_URL, + BAD_URL_SYNTAX, + UNSUPPORTED_URL_SCHEME, + AUTHENTICATION_NOT_SUPPORTED, + FRAGMENT_NOT_SUPPORTED, + INVALID_PORT_NUMBER, + NO_HOST_SPECIFIED, + IPV6_NOT_SUPPORTED, + HOSTNAME_TOO_LONG, + NAME_LOOKUP_FAILED, + OUT_OF_MEMORY +}; + +enum SetURLResult +SetURL(Session *sess, char *url, Boolean permissive, Boolean partialOK); + +#endif diff --git a/strncasecmp.c b/strcasecmp.c similarity index 51% rename from strncasecmp.c rename to strcasecmp.c index 8318ad3..821bbfd 100644 --- a/strncasecmp.c +++ b/strcasecmp.c @@ -5,12 +5,23 @@ #include #include +int strcasecmp(const char *s1, const char *s2) +{ + while (*s1 != '\0' && tolower(*s1) == tolower(*s2)) { + s1++; + s2++; + } + + return (int)*s1 - (int)*s2; +} + + int strncasecmp(const char *s1, const char *s2, size_t n) { if (n == 0) return 0; - while (n > 1 && *s1 != 0 && tolower(*s1) == tolower(*s2)) { + while (n > 1 && *s1 != '\0' && tolower(*s1) == tolower(*s2)) { s1++; s2++; n--; diff --git a/strcasecmp.h b/strcasecmp.h new file mode 100644 index 0000000..81eee39 --- /dev/null +++ b/strcasecmp.h @@ -0,0 +1,9 @@ +#ifndef STRCASECMP_H +#define STRCASECMP_H + +#include + +int strcasecmp(const char *s1, const char *s2); +int strncasecmp(const char *s1, const char *s2, size_t n); + +#endif diff --git a/strncasecmp.h b/strncasecmp.h deleted file mode 100644 index ae20a64..0000000 --- a/strncasecmp.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef STRNCASECMP_H -#define STRNCASECMP_H - -int strncasecmp(const char *s1, const char *s2, size_t n); - -#endif diff --git a/tcpconnection.c b/tcpconnection.c index 5a50362..80150e6 100644 --- a/tcpconnection.c +++ b/tcpconnection.c @@ -20,11 +20,8 @@ Word StartTCPConnection(Session *sess) { srBuff mySRBuff; LongWord initialTime; - if (TCPIPGetConnectStatus() == FALSE) { - TCPIPConnect(NULL); - if (toolerror()) - return aspNetworkErr; - } + /* End any existing TCP connection */ + EndTCPConnection(sess); sess->ipid = TCPIPLogin(userid(), sess->ipAddr, sess->port, 0, 0x40); diff --git a/urlparser.c b/urlparser.c new file mode 100644 index 0000000..7482682 --- /dev/null +++ b/urlparser.c @@ -0,0 +1,122 @@ +#include "urlparser.h" +#include + +/* + * Parse the URL and break it up into its component parts. + * + * This handles http URLs and other schemes with the same syntax. + * + * It can also handle relative URLs containing an absolute-path reference + * (i.e. starting with /) and URL-like addresses without the scheme prefix. + * In these cases, the portions of the URL not provided will be NULL. + * + * This modifies the original string passed in, breaking it up into components. + */ +URLParts ParseURL(char *url) { + char *sep, sep2; + URLParts urlParts = {0}; + + sep = strrchr(url, '#'); + if (sep) { + *sep = '\0'; + urlParts.fragment = sep + 1; + } + + /* Detect relative URL (absolute-path reference only) */ + if (url[0] == '/') { + if (url[1] != '/') { + url++; + goto pathPart; + } else { + urlParts.errorFound = 1; + return urlParts; + } + } + + sep = strchr(url, ':'); + if (sep) { + urlParts.scheme = url; + *sep = '\0'; + url = sep + 1; + } + + if (urlParts.scheme != NULL) { + if (strncmp(url, "//", 2) == 0) { + url += 2; + } else { + urlParts.errorFound = 1; + return urlParts; + } + } + + urlParts.path = strchr(url, '/'); + if (urlParts.path == NULL) { + urlParts.path = ""; + } else { + *urlParts.path = '\0'; + urlParts.path++; + } + + sep = strchr(url, '@'); + if (sep) { + *sep = '\0'; + urlParts.username = url; + + urlParts.password = strchr(url, ':'); + if (urlParts.password != NULL) { + *urlParts.password = '\0'; + urlParts.password++; + } + + url = sep + 1; + } + + urlParts.host = url; + + /* Handle IPv6 address syntax */ + if (*url == '[') { + sep = strchr(url, ']'); + if (sep) { + url = sep + 1; + } else { + urlParts.errorFound = 1; + return urlParts; + } + } + + sep = strchr(url, ':'); + if (sep) { + *sep = '\0'; + urlParts.port = sep + 1; + } + +pathPart: + return urlParts; +} + + +#ifdef URLPARSER_TEST +#include + +int main(int argc, char **argv) +{ + URLParts urlParts; + + if (argc < 2) + return 1; + + urlParts = ParseURL(argv[1]); + + printf("scheme: %s\n", urlParts.scheme ? urlParts.scheme : "(NULL)"); + printf("username: %s\n", urlParts.username ? urlParts.username : "(NULL)"); + printf("password: %s\n", urlParts.password ? urlParts.password : "(NULL)"); + printf("host: %s\n", urlParts.host ? urlParts.host : "(NULL)"); + printf("port: %s\n", urlParts.port ? urlParts.port : "(NULL)"); + printf("path: %s\n", urlParts.path ? urlParts.path : "(NULL)"); + printf("fragment: %s\n", urlParts.fragment ? urlParts.fragment : "(NULL)"); + + if (urlParts.errorFound) { + printf("Error found\n"); + } +} +#endif diff --git a/urlparser.h b/urlparser.h new file mode 100644 index 0000000..091cc71 --- /dev/null +++ b/urlparser.h @@ -0,0 +1,25 @@ +#ifndef URLPARSER_H +#define URLPARSER_H + +#ifdef __ORCAC__ +# include +#else +typedef _Bool Boolean; +#endif + +typedef struct URLParts { + char *scheme; + char *username; + char *password; + char *host; + char *port; + char *path; /* Omits leading '/'. Includes query, if any. */ + char *fragment; + + Boolean errorFound; +} URLParts; + + +URLParts ParseURL(char *url); + +#endif