#ifdef WIN32 #include #endif /* WIN32 */ #include "HT.h" #include "HTChunk.h" #include "HTParse.h" #include "tcp.h" #ifndef WIN32 #include "netinet/tcp.h" #endif /* WIN32 */ #include "../Client/c_def.h" /* debug_level is defined by module that calls http_method */ extern int debug_level; static void get_host_port(char *, char **, int *); #ifdef EXPLICIT_BIND #ifndef WIN32 typedef int SOCKET; #endif /* WIN32 */ static int assign_port_number(SOCKET s); #endif /* EXPLICIT_BIND */ static THREAD http_info *http = NULL; PRIVATE int HTTPCleanup (http_info * http); #ifdef WIN32 #define PRINT_ERR_LINENO fprintf(p_stderr, "runtime error in %s line %d neterr = %d\n", __FILE__, __LINE__, WSAGetLastError()) #else #define PRINT_ERR_LINENO fprintf(p_stderr, "runtime error in %s line %d neterr = %d\n", __FILE__, __LINE__, errno); #endif /* WIN32 */ int HTLoadHTTP (HTRequest *request) { int status = -1; int retval = 0; if (http == NULL) { /* create http_info */ if ((http = (http_info *) calloc(1, sizeof(http_info))) == NULL) { fprintf(p_stderr, "HTOps:HTLoadHTTP: http_info: out of memory error"); PRINT_ERR_LINENO; return -1; } http->isoc = HTInputSocket_new(-1); if (http->isoc == NULL) { fprintf(p_stderr, "HTOps:HTLoadHTTP: socketnew: out of memory error"); PRINT_ERR_LINENO; return -1; } } http->connect = 0; http->isoc->input_file_number = http->sockfd = -1; http->isoc->input_pointer = http->isoc->input_limit = http->isoc->input_buffer; http->request = request; status = HTTPDoConnect(http); if (status < 0) { /* fprintf(p_stderr, "HTOps:HTLoadHTTP: Error in HTDoConnect\n"); */ retval = HTTPCleanup(http); return status; } status = HTTPSendRequest(http); if (status < 0) { /* fprintf(p_stderr, "HTOps:HTLoadHTTP: Error in HTTPSendRequest\n"); */ retval = HTTPCleanup(http); return status; } status = HTTPGetReply(http); if (status < 0) { /* fprintf(p_stderr, "HTOps:HTLoadHTTP: Error in HTTPGetReply\n"); */ retval = HTTPCleanup(http); return status; } retval = HTTPCleanup(http); return status; } /* the following 5 declarations moved/added here 9-98 to eliminate extra gethostbyname() calls */ int HostNameResolvedd = 0; struct hostent *hostelement; char *host_port; char *host; int port; int HTTPDoConnect ( http_info * http ) { THREAD static int conn_num = 0; /* connection attempt # */ SockA sock_addr; /* SockA is defined in tcp.h */ int status; int tcpnodelay; #ifdef EXPLICIT_BIND int numtries; #endif /* EXPLICIT_BIND */ if (debug_level >= 10) { fprintf(p_stderr, "HTTPDoConnect: enter\n"); } if (http->request->keep_alive > 0) { if (http->connect > 0) { return 1; } } /* the following code changed/rearranged 9-98 to eliminate extra gethostbyname() calls */ if (!HostNameResolvedd) { host_port = HTParse(http->request->url, "", PARSE_HOST); if (debug_level >= 10) { fprintf(p_stderr, "HTDoConnect: host_port = %s\n", host_port); } get_host_port(host_port, &host, &port); /* free(host_port); */ #ifndef WIN32 if ((hostelement = gethostbyname(host)) == NULL) { #else if ((hostelement = gethostinfo(host)) == NULL) { #endif /* WIN32 */ fprintf(p_stderr, "HostByName.. Can't find internet node name `%s'.\n", host); PRINT_ERR_LINENO; return -1; } HostNameResolvedd = 1; } /* end of changes 9-98 */ memset((void *) &sock_addr, '\0', sizeof(sock_addr)); sock_addr.sin_family = AF_INET; if (port > 0) { sock_addr.sin_port = htons(port); } else { sock_addr.sin_port = htons((short) 80); } if (debug_level >= 10) { fprintf(p_stderr, "HTDoConnect: host = %s port = %d\n", host, ntohs(sock_addr.sin_port)); } if ((http->sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { fprintf(p_stderr, "No socket"); PRINT_ERR_LINENO; return -1; } http->isoc->input_file_number = http->sockfd; tcpnodelay = 1; if (setsockopt(http->sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &tcpnodelay, sizeof(tcpnodelay)) < 0) { fprintf(p_stderr, "Can't set sockopt TCP_NODELAY"); PRINT_ERR_LINENO; return -1; } #ifdef EXPLICIT_BIND /* explicitly bind the socket to a port */ if ((numtries = assign_port_number(http->sockfd)) < 0) { printf(" %5d* ", conn_num); /* note no \n */ return -1; } else { if (numtries > 10) printf("Numtries = %d\n", numtries); } #endif /* EXPLICIT_BIND */ memcpy(&sock_addr.sin_addr, hostelement->h_addr, hostelement->h_length ); conn_num++; status = connect(http->sockfd, (struct sockaddr *) &sock_addr, sizeof(sock_addr)); if (debug_level >= 10) { fprintf(p_stderr, "HTDoConnect: leave; status = %d\n", status); } if (status < 0) PRINT_ERR_LINENO; return status; } #define CONN_COMMAND "Connection: Keep-Alive\r\n" #define CONN_COMMAND_LEN sizeof(CONN_COMMAND) #define ACCEPT_COMMAND "Accept: */* HTTP/1.0\r\n\r\n" #define ACCEPT_COMMAND_LEN sizeof(ACCEPT_COMMAND) int HTTPSendRequest ( http_info * http ) { int status = 0; char *path; HTChunk *command; char *method; char buf[256], *bufp = buf; if (http->request->keep_alive > 0) { http->connect += 1; } #define NEW 1 #ifdef NEW if (http->request->method != METHOD_INVALID) { method = HTMethod_name(http->request->method); } else method = "GET"; path = strchr(http->request->url, '/')+1; path = strchr(path, '/')+1; path = strchr(path, '/')+1; bufp += sprintf(bufp, "%s /%s HTTP/1.0\r\n%s%s", method, path, (http->request->keep_alive ? CONN_COMMAND : ""), ACCEPT_COMMAND ); /* Now, we are ready for sending the request */ if ((status = NETWRITE(http->sockfd, buf, bufp-buf))<0) { if (TRACE) fprintf(p_stderr, "HTTP Tx..... Error sending command\n"); } #else command = HTChunkCreate(2048); /* The whole command */ if (http->request->method != METHOD_INVALID) { HTChunkPuts(command, HTMethod_name(http->request->method)); HTChunkPutc(command, ' '); } else HTChunkPuts(command, "GET "); HTChunkPuts(command, "/"); path = HTParse(http->request->url, "", PARSE_PATH); HTChunkPuts(command, path); HTChunkPuts(command, " HTTP/1.0"); HTChunkPutc(command, CR); /* CR LF, as in rfc 977 */ HTChunkPutc(command, LF); if (http->request->keep_alive > 0) { HTChunkPuts(command, CONN_COMMAND); } HTChunkPuts(command, ACCEPT_COMMAND); HTChunkTerminate(command); /* Now, we are ready for sending the request */ if ((status = NETWRITE(http->sockfd, command->data, command->size-1))<0) { PRINT_ERR_LINENO; if (TRACE) fprintf(p_stderr, "HTTP Tx..... Error sending command\n"); } HTChunkFree(command); #endif /* NEW */ return status; } #define VERSION_LENGTH 10 #define LINE_LENGTH 100 int HTTPGetReply ( http_info * http ) { int expect_size = 0; int server_status = 0; int ret_count = 0; int len = 0; int content_length = 0; int got_content_length = 0; int count = 0; int prev_count; char server_version[VERSION_LENGTH]; char *status_line = NULL; char *block = NULL; char valname[LINE_LENGTH]; char *valchar = valname; int stat_len; int rv; if (debug_level >= 10) { fprintf(p_stderr, "HTTPGetReply: enter\n"); fprintf(p_stderr, "HTTPGetReply: access_speed = %d validate = %d\n", http->request->access_speed, http->request->validate); } status_line = HTInputSocket_getStatusLine(http->isoc); if (status_line == NULL){ PRINT_ERR_LINENO; goto error; } if (debug_level >= 10) { fprintf(p_stderr, "HTTPGetReply: status line: %s\n", status_line); } ret_count = sscanf(status_line, "HTTP/%s %d", server_version, &server_status); if(ret_count != 2) { fprintf(p_stderr, "HTTPGetReply: bad status line: %s\n", status_line); } free(status_line); if (ret_count == 2) { if (debug_level >= 10) { fprintf(p_stderr, "HTTPGetReply: version %s status %d\n", server_version, server_status); } switch (server_status/100) { case 2: break; case 3: case 4: case 5: default: fprintf(p_stderr, "HTTPGetReply: Status %d for URL \"%s\"\n", server_status, http->request->url); goto error; break; } } else { PRINT_ERR_LINENO; goto error; } /* get header lines until empty line */ while(1) { status_line = HTInputSocket_getLine(http->isoc); if (status_line == NULL){ PRINT_ERR_LINENO; goto error; } if (debug_level >= 10) { fprintf(p_stderr, "HTTPGetReply: line: %s\n", status_line); } if (sscanf(status_line, "Content-length: %d", &content_length) > 0) { got_content_length = 1; if (debug_level >= 10) { fprintf(p_stderr, "HTTPGetReply: content_length: %d\n", content_length); } } if (*status_line == '\0'){ if (debug_level >= 10) { fprintf(p_stderr, "HTTPGetReply: empty line\n"); } free(status_line); break; } free(status_line); } if (http->request->validate) { /* get the expected file size & file name from the first line of the file */ status_line = HTInputSocket_getLine(http->isoc); if (status_line == NULL){ PRINT_ERR_LINENO; goto error; } if (debug_level >= 10) { fprintf(p_stderr, "HTTPGetReply: size line: %s\n", status_line); } ret_count = sscanf(status_line, "%d %s", &expect_size, valname); stat_len = strlen(status_line)+2; free(status_line); if (ret_count != 2) { PRINT_ERR_LINENO; goto error; } if (debug_level >= 10) { fprintf(p_stderr, "HTTPGetReply: validate url = %s\n", valname); fprintf(p_stderr, "HTTPGetReply: expect_size %d\n", expect_size); } } #define CHUNKSIZE 4096 /* assumes that the maximum block read (INPUT_BUFFER_SIZE) * is <= the verification interval (CHUNKSIZE) */ prev_count = CHUNKSIZE-1; while (1) { len = INPUT_BUFFER_SIZE; block = HTInputSocket_getBlock(http->isoc, &len); count += len; if (debug_level >= 10) { fprintf(p_stderr, "HTTPGetReply: got %d bytes\n", len); } if (block == NULL) { break; } if (http->request->validate && prev_count+len >= CHUNKSIZE) { if (*valchar++ == block[(CHUNKSIZE-1)-prev_count]) { /* Check if match validates */ if (*valchar == '\0') { /* check for end of string */ valchar = valname; /* if so, start over again */ } } else { /* block does not match url */ if (debug_level >= 10) { fprintf(p_stderr, "HTTPGetReply: mismatch '%c' (%d) != '%c' (%d)\n", (int)(*valchar),(int)(*valchar), (int)(block[0]), (int)(block[0])); } PRINT_ERR_LINENO; goto error; /* a fetch error */ } prev_count -= CHUNKSIZE; } prev_count += len; #define CLOSE_ON_COUNT 1 #ifdef CLOSE_ON_COUNT if (got_content_length) { #else if (http->request->keep_alive > 0) { #endif /* CLOSE_ON_COUNT */ if (count+stat_len == content_length) { break; } } } if (debug_level >= 10) { fprintf(p_stderr, "HTTPGetReply: count %d\n", count); } count += stat_len; if (http->request->validate && (count != expect_size)) { PRINT_ERR_LINENO; goto error; } rv = 1; goto exit; error: rv = -1; exit: return rv; } PRIVATE int HTTPCleanup (http_info * http) { int status = 0; if (http->request->keep_alive > 0){ if (debug_level >= 10) { fprintf(p_stderr, "HTTPCleanup: connect = %d keep_alive = %d\n", http->connect, http->request->keep_alive); } if (http->connect < http->request->keep_alive) { return 1; } else { http->connect = 0; } } if ((status = NETCLOSE(http->sockfd)) < 0){ PRINT_ERR_LINENO; fprintf(p_stderr, "HTTPCleanup: close error\n"); } return status; } PRIVATE char * method_names[(int)MAX_METHODS + 1] = { "INVALID-METHOD", "GET", "HEAD", "POST", "PUT", "DELETE", "CHECKOUT", "CHECKIN", "SHOWMETHOD", "LINK", "UNLINK", NULL }; PUBLIC HTMethod HTMethod_enum ARGS1(char *, name) { if (name) { int i; for (i=1; i < (int)MAX_METHODS; i++) if (!strcmp(name, method_names[i])) return (HTMethod)i; } return METHOD_INVALID; } /* Get method name ** --------------- */ PUBLIC char * HTMethod_name ARGS1(HTMethod, method) { if ((int)method > (int)METHOD_INVALID && (int)method < (int)MAX_METHODS) return method_names[(int)method]; else return method_names[(int)METHOD_INVALID]; } /* * host_port is of the form host:port or just host */ static void get_host_port( char *host_port, char **host, int *port ) { char *p; *host = (char *) strdup(host_port); p = strchr(*host, ':'); if (p == NULL) { *port = -1; return; } else { *p = '\0'; p++; *port = atoi(p); } } #ifdef EXPLICIT_BIND #define MINPORT 5001L /* lowest port number to assign */ #define MAXPORT 65000L /* highest port number to assign */ /* Assign a port number to an unbounded socket Each child gets its own unique range of port number via % operation Return -1 for failure, otherwise the number of extra sockets checked */ static int assign_port_number(SOCKET s) { #ifdef WIN32 SOCKADDR_IN sin; THREAD static long port_req = -1; #else struct sockaddr_in sin; static long port_req = -1; #endif /* WIN32 */ u_short port; int i; extern int children; /* # of children on this system */ sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; if (port_req == -1) port_req = Child_num; /* initialize */ for (i = 0; i <= (MAXPORT-MINPORT) % children; i++) { port = (u_short)((port_req % (1+MAXPORT-MINPORT)) + MINPORT); port_req += children; sin.sin_port = htons(port); #ifdef WIN32 if (bind(s, (LPSOCKADDR)&sin, sizeof (sin)) == 0) { #else if (bind(s, (struct sockaddr_in *)&sin, sizeof (sin)) == 0) { #endif /* WIN32 */ /* port assignment worked */ return i; } #ifdef WIN32 if ( GetLastError() != WSAEADDRINUSE) { #else if (errno = EADDRINUSE) { #endif /* WIN32 */ /* fail from cause other than lack of a port */ return -1; } /* this seems to be unused? raj */ /* numskips++; */ } /* fail--all port numbers are in use */ return -1; } #endif /* EXPLICIT_BIND */