/* * httpget.c - Get a URL. * * Compile/link under SunOS with "gcc -O -DSunOS -o httpget httpget.c" * Compile/link under Linux with "gcc -O -DUNIX -o httpget httpget.c" * */ #include #include #include #include #include "getopt.h" #define DEFAULT_NET_TIMEOUT 20 #ifdef SunOS #define UNIX #endif #if defined(UNIX) #include #include #include #include #ifdef SunOS #include #include #else #include #include #endif #include #include #include #include #include #define SockClose(x) close(x) #ifndef O_BINARY #define O_BINARY 0 #endif #endif #if defined(_CONSOLE) #include #include #include #include #define SockClose(x) closesocket(x) #define sleep(x) Sleep((x)*1000L) #define NetErrno (WSAGetLastError()) #else #define NetErrno ((long)errno) #endif #if !defined(TRUE) #define TRUE (0==0) #endif #if !defined(FALSE) #define FALSE (0!=0) #endif #define STRNCPY(_d,_s) strncpy(_d,_s,sizeof _d) ; _d[sizeof _d-1] = 0 #define HTTP_PORT 80 static int debug = FALSE; static int bForce = FALSE; static int quiet = FALSE; static int map = FALSE ; static int noheader = FALSE ; typedef struct { int ReadURL ; /* TRUE if should read URL */ int Record ; /* record # 1..n */ int HttpStatus ; /* status from HTTP reply record */ #define HTTP_GET_OK 200 #define HTTP_POST_OK 302 /* 302 HTTP/1.1 302 Found */ int VerMajor ; /* major version number */ int VerMinor ; /* minor version number */ char StatusInfo[1024] ; /* status information */ } HTTP_DECODE_INFO ; static void Error( char *sWhat ){ #if defined(UNIX) #if 1 if (!quiet) perror(sWhat); #else if (!quiet) fprintf(stderr,"%s: %s\n",sWhat,strerror(errno)) ; #endif #endif #if defined(_CONSOLE) if (!quiet) fprintf(stderr,"%s: error %lu\n",sWhat,WSAGetLastError()) ; #endif exit(9) ; } static int DoHttpDecode( HTTP_DECODE_INFO *p, char *sHttp, int Len ){ p->Record++ ; /* count record */ if (Len == 0) { /* empty */ return(1) ; /* signal outer layer to stop reading */ } else if (strncmp(sHttp,"HTTP",4) == 0) { /* * if it looks like status info, then go ahead and * decode the remainder. we think it looks like "HTTP/n.m xxx" * where n.m is a version, and xxx is some sort of status info. */ STRNCPY(p->StatusInfo,sHttp) ; /* save for later */ if (sscanf(&sHttp[4],"/%d.%d %d",&p->VerMajor,&p->VerMinor, &p->HttpStatus) == 3) { /* looks good */ if ((p->HttpStatus == HTTP_GET_OK) || (p->HttpStatus == HTTP_POST_OK)) { p->ReadURL = TRUE ; } } } else { /* add processing of other records here. */ } return(0) ; /* keep reading */ } static int RecvWithTimeout( int Socket, char *Buffer, int Len, long Timeout, int *bTimedOut ){ fd_set ReadSet ; int n ; struct timeval Time ; FD_ZERO(&ReadSet) ; FD_SET(Socket,&ReadSet) ; Time.tv_sec = Timeout ; Time.tv_usec = 0 ; *bTimedOut = FALSE ; n = select(Socket+1,&ReadSet,NULL,NULL,&Time) ; if (n > 0) { /* got some data */ return(recv(Socket,Buffer,Len,0)) ; } if (n == 0) { /* timeout */ *bTimedOut = TRUE ; } return(n) ; /* trouble */ } static unsigned char * ReadPostData( int map_special_chars, char *sFile, int *nBytes ){ # define POST_CHUNK_BYTES 2048 int fd ; int nread ; int quote = TRUE ; int n ; int ch ; int buf_size ; unsigned char *buf ; unsigned char *pbuf ; *nBytes = 0 ; if (sFile == NULL) return NULL ; buf_size = POST_CHUNK_BYTES ; if (!(buf = (unsigned char *)malloc(buf_size))) { Error("no memory") ; return NULL ; } else { pbuf = buf ; } if (strcmp(sFile,"-") == 0) { fd = 0 ; } else { if ((fd = open(sFile, O_RDONLY | O_BINARY)) < 0) { if (!quiet) { fprintf(stderr, "can't open POST file ") ; Error(sFile) ; free(buf) ; return NULL ; } } } while (1) { n = pbuf - buf ; if ((buf_size-n+3) < POST_CHUNK_BYTES) { buf_size += POST_CHUNK_BYTES ; if (!(buf = (unsigned char *)realloc(buf,buf_size))) { Error("no memory") ; free(buf) ; return NULL ; } else { pbuf = buf + n ; } } if (map_special_chars) { nread = 1 ; } else { nread = buf_size-(pbuf-buf) ; } if ((n=(int)read(fd,pbuf,nread)) < 0) { if (!quiet) Error("read POST file") ; close(fd) ; free(buf) ; return NULL ; } if (n == 0) break ; if (quote == TRUE && map_special_chars) { ch = *pbuf ; if (ch == ' ') { *pbuf = '+' ; } else if (ch == '\n') { *pbuf = '&' ; } else if (ch == '\r') { n = 0 ; } else if (ch == '\\') { quote = FALSE ; n = 0 ; } else if (ch == '@') { ; } else if (ch == '=') { ; } else if (ch == '_') { ; } else if (ch == '*') { ; } else if (ch >= '-' && ch <= '9') { ; } else if (ch >= 'A' && ch <= 'Z') { ; } else if (ch >= 'a' && ch <= 'z') { ; } else { sprintf(pbuf, "%%%02X", ch) ; n = 3 ; } } else { quote = TRUE ; } pbuf += n ; } close(fd) ; #ifdef ADD_TERMINATOR *(pbuf++) = '\r'; *(pbuf++) = '\n'; #endif *nBytes = pbuf - buf ; return buf ; } static int SetSockBlock( int Socket, int iBlocking ){ u_long Nbio ; if (iBlocking) { Nbio = 0 ; } else { Nbio = 1 ; } #if defined(WIN32) return(ioctlsocket(Socket,FIONBIO,&Nbio)) ; #else return(ioctl(Socket,FIONBIO,&Nbio)) ; #endif } #define WR_READ 1 #define WR_WRITE 2 #define WR_EXCEPT 4 static int WaitReady( int Socket, int Timeout, int which ){ fd_set ReadSet ; int n ; struct timeval Time ; fd_set *pRead = NULL ; fd_set *pWrite = NULL ; fd_set *pExcept = NULL ; if (which&WR_READ) pRead = &ReadSet ; if (which&WR_WRITE) pWrite = &ReadSet ; if (which&WR_EXCEPT) pExcept = &ReadSet ; FD_ZERO(&ReadSet) ; FD_SET(Socket,&ReadSet) ; Time.tv_sec = Timeout ; Time.tv_usec = 0 ; n = select(Socket+1,pRead,pWrite,pExcept,&Time) ; if (n > 0) { /* got some data */ return(0) ; } if (n == 0) { /* timeout */ #if defined(WIN32) WSASetLastError(WSAETIMEDOUT) ; #else errno = ETIMEDOUT ; #endif return(1) ; } return(-1) ; } main( int argc, char *argv[] ){ long TimeoutSeconds = DEFAULT_NET_TIMEOUT ; int bTimeout = FALSE ; int bAddResultHdr = FALSE; int bExit = 0 ; char *sURL ; char *sName ; int Socket ; char *s ; char *sSocks = NULL ; char *sProxy = NULL ; char *sPost = NULL ; char *sPostData = NULL ; char *sCookie = NULL ; char *sRefer = NULL ; int nPost = 0 ; int n ; char sURLHost[256] ; char sHost[256] ; char sExtra[128] ; char sCookieText[1024] ; char sReferText[1024] ; char sGenericText[1024] = ""; int iConnTimeout = 0 ; int ConnStatus ; struct sockaddr_in SockAddr ; struct hostent *pHostEnt ; struct { unsigned char vn ; unsigned char fc ; unsigned short port ; unsigned long addr ; char username[32] ; } SocksConnect = { 4, 1, 0, 0, "" } ; struct { unsigned char vn ; unsigned char cd ; unsigned short port ; unsigned long addr ; } SocksReply ; #if defined(_CONSOLE) WORD wVer ; WSADATA wsaData ; #endif struct linger linger; char sHttp[1024] ; int c ; HTTP_DECODE_INFO Http ; linger.l_onoff = 1 ; linger.l_linger = 5 ; #if defined(_CONSOLE) wVer = MAKEWORD(1,1) ; // request WinSock version 1.1 if (WSAStartup(wVer,&wsaData) != 0) { // if startup failed return(2) ; } #endif #define CMD_SWITCHES "drfqmhp:s:t:P:c:C:R:g:?" while ((c = getopt(argc,argv,CMD_SWITCHES)) != EOF) { switch (c) { case 'd': { debug = 1 ; #if defined(WIN32) setvbuf(stdout,NULL,_IOLBF,0) ; setvbuf(stderr,NULL,_IOLBF,0) ; #endif break ; } case 't': { iConnTimeout = atoi(optarg) ; break ; } case 'f': { bForce = TRUE ; break ; } case 'q': { quiet = TRUE ; break ; } case 'm': { map = TRUE ; break ; } case 'h': { bAddResultHdr = TRUE ; break ; } case 's': { sSocks = optarg ; break ; } case 'p': { sProxy = optarg ; break ; } case 'C': { TimeoutSeconds = atoi(optarg) ; break ; } case 'P': { sPost = optarg ; break ; } case 'c': { sCookie = optarg ; break ; } case 'R': { sRefer = optarg ; break ; } case 'r': { noheader = 1 ; break ; } case 'g': { if ( (strlen(sGenericText) + strlen(optarg) + 3) > sizeof(sGenericText) ) { fprintf( stderr, "ABORT: sGenericText overrun\n" ); exit(1); } sprintf( sGenericText, "%s%s\r\n", sGenericText, optarg ); break ; } default: { goto Usage ; } } } if (argc < (optind+1)) { Usage: ; fprintf(stderr, "Usage:\t httpget [options] URL\n" "options:\n" " -d - set debug mode\n" " -q - set quiet mode (no error printouts)\n" " -f - force data read on bad HTML\n" " -h - add HTTP result header to output stream\n" " -s host[:port] - use SOCKS server \"host\" (optional port)\n" " -p host[:port] - use proxy server \"host\" (optional port)\n" " -C secs - set network read timeout in seconds\n" " -t secs - set network connect timeout in seconds\n" " -P file - POST file to URL (use \"-\" for stdin)\n" " -m - map special characters in POST data (\\ quotes)\n" " -c cookie - send cookie string with request\n" " -R referringURL - send referring URL with request\n" " -g generic - include specified generic header\n" "where URL is of the form \"http://host[:port]/path\"\n" ); return(10) ; } if (sPost != NULL) { if ((sPostData = ReadPostData(map,sPost,&nPost)) == NULL) { return(4) ; } if (debug) fprintf(stderr,"%d bytes read from POST file %s\n",nPost,sPost) ; bAddResultHdr = TRUE ; } memset((void *)&SockAddr,0,sizeof SockAddr) ; /* zero sockaddr */ SockAddr.sin_family = AF_INET ; SockAddr.sin_port = htons(HTTP_PORT) ; sURL = argv[optind] ; if ((s=strstr(argv[optind],"//")) != NULL) { s += 2 ; /* skip leading stuff - better be http:// */ } else { s = argv[optind] ; } if ((sName=strchr(s,'/')) == NULL) { /* save URL "name part" */ sName = "/" ; } STRNCPY(sHost, s); /* isolate URL host name */ if ((s=strchr(sHost,'/')) != NULL) { *s = 0 ; } STRNCPY(sURLHost, sHost); /* save URL host name/port */ if (sProxy != NULL) { /* switch to proxy server? */ STRNCPY(sHost, sProxy); } if (debug) fprintf(stderr,"Host: %s, URL Host: %s, Name: %s\n",sHost,sURLHost,sName) ; if ((s = strchr(sHost,':')) != NULL) { /* isolate target host port */ *s++ = 0 ; SockAddr.sin_port = htons((u_short)atoi(s)) ; } if (isdigit(sHost[0])) { /* by address */ SockAddr.sin_addr.s_addr = inet_addr(sHost) ; } else { /* by name - need resolver */ pHostEnt = gethostbyname(sHost) ; if (pHostEnt == NULL) { /* hostname lookup error */ fprintf(stderr,"%s: unknown host\n",sHost) ; return(1) ; } SockAddr.sin_addr = *((struct in_addr *)pHostEnt->h_addr_list[0]) ; } if (sSocks != NULL) { char *user ; SocksConnect.port = SockAddr.sin_port ; SocksConnect.addr = SockAddr.sin_addr.s_addr ; user = getenv("USER") ; if (!user) user = "SSL" ; STRNCPY(SocksConnect.username,user) ; SocksConnect.username[sizeof(SocksConnect.username)-1] = 0 ; SockAddr.sin_port = htons(1080) ; s = strchr(sSocks,':') ; if (s != NULL) { *s++ = 0 ; SockAddr.sin_port = htons((u_short)atoi(s)) ; } if (isdigit(sSocks[0])) { /* by address */ SockAddr.sin_addr.s_addr = inet_addr(sSocks) ; } else { /* by name - need resolver */ pHostEnt = gethostbyname(sSocks) ; if (pHostEnt == NULL) { /* hostname lookup error */ fprintf(stderr,"%s: unknown host\n",sSocks) ; return(2) ; } SockAddr.sin_addr = *((struct in_addr *)pHostEnt->h_addr_list[0]) ; } } Socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP) ; if (Socket != -1) { if (setsockopt(Socket, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger)) < 0) { Error("setting linger") ; } if (sSocks == NULL && iConnTimeout != 0) { int iLen = sizeof ConnStatus ; SetSockBlock(Socket,0) ; /* non-blocking */ ConnStatus = connect(Socket,(struct sockaddr *)&SockAddr, sizeof SockAddr) ; if (debug) { fprintf(stderr,"NB connect status %lu\n",NetErrno) ; } SetSockBlock(Socket,1) ; /* back to blocking */ ConnStatus = WaitReady(Socket,iConnTimeout,WR_WRITE) ; if (ConnStatus > 0) { ConnectionTimeout: ; if (debug) { fprintf(stderr,"Connection timeout.\n") ; } ConnStatus = -1 ; } } else { ConnStatus = connect(Socket,(struct sockaddr *)&SockAddr, sizeof SockAddr) ; } if (ConnStatus == 0) { if (sSocks != NULL) { send(Socket,(void *)&SocksConnect,(sizeof SocksConnect -sizeof SocksConnect.username)+ strlen(SocksConnect.username)+1,0) ; if (iConnTimeout != 0) { if (WaitReady(Socket,iConnTimeout,WR_READ) > 0) { goto ConnectionTimeout ; } } n = recv(Socket,(void *)&SocksReply,sizeof SocksReply,0) ; if (n == sizeof SocksReply && SocksReply.cd == 90) { if (debug) fprintf(stderr,"Connected via Socks gateway %s\n",sSocks) ; } else { fprintf(stderr,"Socks connect failed.\n") ; linger.l_onoff = 1 ; linger.l_linger = 0 ; if (setsockopt(Socket, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger)) < 0) { Error("setting linger") ; } goto bomb ; } } if (sPost != NULL) { sprintf(sExtra, "Content-type: application/x-www-form-urlencoded\r\n" #ifdef ADD_TERMINATOR "Content-length: %d\r\n",nPost - 2); #else "Content-length: %d\r\n",nPost); #endif } if (sCookie != NULL) { sprintf(sCookieText, "Cookie: %s\r\n",sCookie); } if (sRefer != NULL) { sprintf(sReferText, "Referer: %s\r\n",sRefer); } sprintf(sHttp, "%s %s HTTP/1.0\r\n" "%sUser-Agent: Mozilla/2.0 (Win95; I)\r\n" "Pragma: no-cache\r\n" "Host: %s\r\n" "Accept: */*\r\n" "%s%s%s%s\r\n%n", (sPost == NULL) ? "GET" : "POST", (sProxy == NULL) ? ((sPost == NULL) ? sName : sURL) : sURL, (sProxy == NULL && sSocks == NULL) ? "" : "Proxy-Connection: Keep-Alive\r\n", sURLHost, (sRefer == NULL) ? "" : sReferText, (sCookie == NULL) ? "" : sCookieText, sGenericText, (sPost == NULL) ? "" : sExtra, &n) ; if (debug) fprintf(stderr,"%s",sHttp) ; if (!noheader) { if (send(Socket,sHttp,n,0) != n) { if (!quiet) Error("send") ; bomb: ; shutdown(Socket,2) ; SockClose(Socket) ; return(3) ; } } /* * POST any form data */ if (sPost != NULL) { if (debug) { fflush(stderr) ; write(2,sPostData,nPost); } if (debug) fprintf(stderr,"POSTING %d bytes...\n",nPost) ; if (send(Socket,sPostData,nPost,0) != nPost) { if (!quiet) Error("send POST data") ; free(sPostData) ; goto bomb ; } free(sPostData) ; } /* * loop through LF delimited records and process each one. * ignore CRs completely. pass each record to the decoder and * let it tell us when we're done. */ memset(&Http,0,sizeof Http) ; n = 0 ; if (!noheader) { while (n < sizeof sHttp && RecvWithTimeout(Socket,&sHttp[n],1, TimeoutSeconds,&bTimeout) > 0) { if (bAddResultHdr) { fprintf(stdout,"%c",sHttp[n]) ; } else if (debug) { fprintf(stderr,"%c",sHttp[n]) ; fflush(stderr) ; } if (sHttp[n] == '\r') { /* CR - must be MicrosOffed */ continue ; } if (sHttp[n] == '\n') { /* another record */ sHttp[n] = 0 ; if (DoHttpDecode(&Http,sHttp,n) != 0) { break ; /* stop decoding */ } n = 0 ; } else { n++ ; } } if (!bTimeout && !Http.ReadURL) { if (!quiet) { fprintf(stderr,"Bad status: %d %s\n", Http.HttpStatus,Http.StatusInfo) ; fflush(stderr) ; } bExit = 1 ; } } else { Http.ReadURL = TRUE ; } /* * now, if we are supposed to read this URL, then go ahead and * copy it to stdout - in binary mode. */ if (bForce || (Http.ReadURL && !bTimeout)) { #if defined(_CONSOLE) _setmode(fileno(stdout),O_BINARY) ; #endif while ((n=RecvWithTimeout(Socket,sHttp,sizeof sHttp, TimeoutSeconds,&bTimeout)) > 0) { if ((int)fwrite(sHttp,1,n,stdout) != n) { if (!quiet) Error("stdout") ; goto bomb ; } } } if (bForce) printf("\n") ; if (n < 0) { Error("reading URL") ; } } else { Error(sURL) ; } SockClose(Socket) ; } else { Error("socket") ; } if (bTimeout) { bExit = 1 ; if (!quiet) fprintf(stderr,"Network read timeout.\n") ; } return(bExit) ; }