wbox-5/0000755000175000017500000000000011311456121010526 5ustar abbaabbawbox-5/wbsignal.h0000644000175000017500000000326511310137727012522 0ustar abbaabba/* Copyright (C) 2007 Salvatore Sanfilippo, antirez@gmail.com * This softare is released under the following BSD license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef WBOX_SIGNAL_H #define WBOX_SIGNAL_H #include void (*Signal(int signo, void (*func)(int)))(int); #endif wbox-5/anet.c0000644000175000017500000001462311310137603011627 0ustar abbaabba/* anet.c -- Basic TCP socket stuff * Copyright (C) 2006,2007 Salvatore Sanfilippo, antirez@gmail.com * This softare is released under the following BSD license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "anet.h" static void anetSetError(char *err, const char *fmt, ...) { va_list ap; if (!err) return; va_start(ap, fmt); vsnprintf(err, ANET_ERR_LEN, fmt, ap); va_end(ap); } int anetNonBlock(char *err, int fd) { int flags; /* Set the socket nonblocking. * Note that fcntl(2) for F_GETFL and F_SETFL can't be * interrupted by a signal. */ if ((flags = fcntl(fd, F_GETFL)) == -1) { anetSetError(err, "fcntl(F_GETFL): %s\n", strerror(errno)); return ANET_ERR; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s\n", strerror(errno)); return ANET_ERR; } return ANET_OK; } int anetTcpNoDelay(char *err, int fd) { int buffsize = 1024*8; if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1) { anetSetError(err, "setsockopt SO_SNDBUF: %s\n", strerror(errno)); return ANET_ERR; } return ANET_OK; } int anetResolve(char *err, char *host, char *ipbuf) { struct sockaddr_in sa; sa.sin_family = AF_INET; if (inet_aton(host, &sa.sin_addr) == 0) { struct hostent *he; he = gethostbyname(host); if (he == NULL) { anetSetError(err, "can't resolve: %s\n", host); return ANET_ERR; } memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); } strcpy(ipbuf,inet_ntoa(sa.sin_addr)); return ANET_OK; } int anetTcpConnect(char *err, char *addr, int port) { int s; struct sockaddr_in sa; if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) { anetSetError(err, "creating socket: %s\n", strerror(errno)); return ANET_ERR; } sa.sin_family = AF_INET; sa.sin_port = htons(port); if (inet_aton(addr, &sa.sin_addr) == 0) { struct hostent *he; he = gethostbyname(addr); if (he == NULL) { anetSetError(err, "can't resolve: %s\n", addr); close(s); return ANET_ERR; } memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); } if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) { anetSetError(err, "connect: %s\n", strerror(errno)); close(s); return ANET_ERR; } return s; } /* Like read(2) but make sure 'count' is read before to return * (unless error or EOF condition is encountered) */ int anetRead(int fd, void *buf, int count) { int nread, totlen = 0; while(totlen != count) { nread = read(fd,buf,count-totlen); if (nread == 0) return totlen; if (nread == -1) return -1; totlen += nread; buf += nread; } return totlen; } /* Like write(2) but make sure 'count' is read before to return * (unless error is encountered) */ int anetWrite(int fd, void *buf, int count) { int nwritten, totlen = 0; while(totlen != count) { nwritten = write(fd,buf,count-totlen); if (nwritten == 0) return totlen; if (nwritten == -1) return -1; totlen += nwritten; buf += nwritten; } return totlen; } int anetTcpServer(char *err, int port, char *bindaddr) { int s, on = 1; struct sockaddr_in sa; if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) { anetSetError(err, "socket: %s\n", strerror(errno)); return ANET_ERR; } if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { anetSetError(err, "setsockopt SO_REUSEADDR: %s\n", strerror(errno)); close(s); return ANET_ERR; } sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = htonl(INADDR_ANY); if (bindaddr) inet_aton(bindaddr, &sa.sin_addr); if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) { anetSetError(err, "bind: %s\n", strerror(errno)); close(s); return ANET_ERR; } if (listen(s, 5) == -1) { anetSetError(err, "listen: %s\n", strerror(errno)); close(s); return ANET_ERR; } return s; } int anetAccept(char *err, int serversock, char *ip, int *port) { int fd; struct sockaddr_in sa; unsigned int saLen; while(1) { saLen = sizeof(sa); fd = accept(serversock, (struct sockaddr*)&sa, &saLen); if (fd == -1) { if (errno == EINTR) continue; else { anetSetError(err, "accept: %s\n", strerror(errno)); return ANET_ERR; } } break; } if (ip) strcpy(ip,inet_ntoa(sa.sin_addr)); if (port) *port = ntohs(sa.sin_port); return fd; } wbox-5/TODO0000644000175000017500000000250010632777350011232 0ustar abbaabbaTODO list for WBox == HTTP CLIENT MODE == High priority . $HOME/.wboxrc . Computer parsable output "type:reply|len:3234|time:3424|code:200..." . Setting arbitrary header via "Headername: value" Low priority . Ability to follow links can be interesting, for example to generate a map of the site, check for broken links, and so on. . "color" option to use terminal colors to make the output more readable . Select segment size in timesplit mode. . POST method . Get urls from file, using the special url file:/tmp/filename.txt and "randomize" option to request a different URL (from file:...) for every request. == HTTP SERVER MODE == High priority . Handle zero length (/proc filesystem is a good example) files . Handle clients in timeout Low priority . Ability to "bind" commands to specific urls. For example using something like 'wbox servermode /tmp/foobar bindurl /uptime uptime" should show the uptime command output accessing http://127.0.0.1:8081/uptime. Multiple binds should be possible at the same time. . A special 'servertest' mode implementing a select(2) or pthread based server with minimal overhead serving a static page (the user should be able to set the length of the generated page). This mode should be useful in order to benchmark a server theoretical limits serving pages of the given size. wbox-5/Changelog0000644000175000017500000000122311310140006012325 0ustar abbaabbaWBox changelog > WBox 5 . License switch: GPLv2 -> New BSD > WBox 4 . option "maxclients" to set max number of concurrent clients in server mode. . option "close" to close the connection after the few block of data was received. This is useful to check what happens on EOF from client conditions in web applications, to test the latency aspect against big pages, and so on. . min/avg/max time statistics on Ctrl+C or termination . "Index of ..." no longer expose the real directory path on the filesystem . source code cleanup . Many fixes, including no more defunct processes in server mode. > WBox 3,2,1 ... sorry, file created starting from version 4 ;) wbox-5/COPYING0000644000175000017500000000313711310137751011571 0ustar abbaabba All the Software contained in this source distribution is Copyright(C) 2007 Salvatore Sanfilippo. All Rights reserved. The Software is released by the author under the following BSD license: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. wbox-5/sds.c0000644000175000017500000001332211310137640011465 0ustar abbaabba/* SDSLib, the C dynamic strings library * Copyright (C) 2006,2007 Salvatore Sanfilippo, antirez@gmail.com * This softare is released under the following BSD license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "sds.h" #include #include #include #include char *sdsnewlen(void *init, size_t initlen) { struct sdshdr *sh; sh = malloc(sizeof(struct sdshdr)+initlen+1); if (sh == NULL) return NULL; sh->len = initlen; sh->free = 0; if (initlen) { if (init) memcpy(sh->buf, init, initlen); else memset(sh->buf,0,initlen); } sh->buf[initlen] = '\0'; return (char*)sh->buf; } char *sdsnew(char *init) { size_t initlen = (init == NULL) ? 0 : strlen(init); return sdsnewlen(init, initlen); } size_t sdslen(char *s) { struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); return sh->len; } char *sdsdup(char *s) { return sdsnewlen(s, sdslen(s)); } void sdsfree(char *s) { if (s == NULL) return; free(s-sizeof(struct sdshdr)); } size_t sdsavail(char *s) { struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); return sh->free; } void sdsupdatelen(char *s) { struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); int reallen = strlen(s); sh->free += (sh->len-reallen); sh->len = reallen; } static char *sdsMakeRoomFor(char *s, size_t addlen) { struct sdshdr *sh, *newsh; size_t free = sdsavail(s); size_t len, newlen; if (free >= addlen) return s; len = sdslen(s); sh = (void*) (s-(sizeof(struct sdshdr))); newlen = (len+addlen)*2; newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1); if (newsh == NULL) return NULL; newsh->free = newlen - len; return newsh->buf; } char *sdscatlen(char *s, void *t, size_t len) { struct sdshdr *sh; size_t curlen = sdslen(s); s = sdsMakeRoomFor(s,len); if (s == NULL) return NULL; sh = (void*) (s-(sizeof(struct sdshdr))); memcpy(s+curlen, t, len); sh->len = curlen+len; sh->free = sh->free-len; s[curlen+len] = '\0'; return s; } char *sdscat(char *s, char *t) { return sdscatlen(s, t, strlen(t)); } char *sdscpylen(char *s, char *t, size_t len) { struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); size_t totlen = sh->free+sh->len; if (totlen < len) { s = sdsMakeRoomFor(s,len-totlen); if (s == NULL) return NULL; sh = (void*) (s-(sizeof(struct sdshdr))); totlen = sh->free+sh->len; } memcpy(s, t, len); s[len] = '\0'; sh->len = len; sh->free = totlen-len; return s; } char *sdscpy(char *s, char *t) { return sdscpylen(s, t, strlen(t)); } char *sdscatprintf(char *s, const char *fmt, ...) { va_list ap; char *buf, *t; size_t buflen = 2; va_start(ap, fmt); while(1) { if ((buf = malloc(buflen)) == NULL) return NULL; buf[buflen-2] = '\0'; vsnprintf(buf, buflen, fmt, ap); if (buf[buflen-2] != '\0') { free(buf); buflen *= 2; continue; } break; } va_end(ap); t = sdscat(s, buf); free(buf); return t; } char *sdstrim(char *s, const char *cset) { struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); char *start, *end, *sp, *ep; size_t len; sp = start = s; ep = end = s+sdslen(s)-1; while(sp < end && strchr(cset, *sp)) sp++; while(ep > start && strchr(cset, *ep)) ep--; len = (sp > ep) ? 0 : ((ep-sp)+1); memmove(sh->buf, sp, len); sh->buf[len] = '\0'; sh->free = sh->free+(sh->len-len); sh->len = len; return s; } char *sdsrange(char *s, long start, long end) { struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); size_t newlen, len = sdslen(s); if (len == 0) return s; if (start < 0) { start = len+start; if (start < 0) start = 0; } if (end < 0) { end = len+end; if (end < 0) end = 0; } newlen = (start > end) ? 0 : (end-start)+1; if (newlen != 0) { if (start >= (signed)len) start = len-1; if (end >= (signed)len) end = len-1; newlen = (start > end) ? 0 : (end-start)+1; } else { start = 0; } memmove(sh->buf, sh->buf+start, newlen); sh->buf[newlen] = 0; sh->free = sh->free+(sh->len-newlen); sh->len = newlen; return s; } wbox-5/wbsignal.c0000644000175000017500000000442611310137242012505 0ustar abbaabba/* wbsignal.c, part of wbox * Copyright (C) 2007 Salvatore Sanfilippo, antirez@gmail.com * This softare is released under the following BSD license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include /* Portable signal() from R.Stevens, * modified to reset the handler */ void (*Signal(int signo, void (*func)(int)))(int) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; /* So if set SA_RESETHAND is cleared */ if (signo == SIGALRM) { #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */ #endif } else { #ifdef SA_RESTART act.sa_flags |= SA_RESTART; /* SVR4, 4.4BSD, Linux */ #endif } if (sigaction(signo, &act, &oact) == -1) return SIG_ERR; return (oact.sa_handler); } wbox-5/AUTHORS0000644000175000017500000000022210630647072011604 0ustar abbaabbaWbox was written by Salvatore 'antirez' Sanfilippo http://invece.org (english) http://antirez.com (italian) email: user antirez, domain gmail.com wbox-5/sds.h0000644000175000017500000000435311310137652011501 0ustar abbaabba/* SDSLib, the C dynamic strings library * Copyright (C) 2006,2007 Salvatore Sanfilippo, antirez@gmail.com * This softare is released under the following BSD license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __SDS_H #define __SDS_H #include typedef char *sds; struct sdshdr { long len; long free; char buf[0]; }; char *sdsnewlen(void *init, size_t initlen); char *sdsnew(char *init); size_t sdslen(char *s); char *sdsdup(char *s); void sdsfree(char *s); size_t sdsavail(char *s); char *sdscatlen(char *s, void *t, size_t len); char *sdscat(char *s, char *t); char *sdscpylen(char *s, char *t, size_t len); char *sdscpy(char *s, char *t); char *sdscatprintf(char *s, const char *fmt, ...); char *sdstrim(char *s, const char *cset); char *sdsrange(char *s, long start, long end); void sdsupdatelen(char *s); #endif wbox-5/Makefile0000644000175000017500000000063611310140353012167 0ustar abbaabba# Makefile for wbox # Copyright (C) 2007 Salvatore Sanfilippo # All Rights Reserved # Under the New BSD license DEBUG?= -g CFLAGS?= -O2 -Wall -W CCOPT= $(CFLAGS) OBJ = anet.o sds.o wbsignal.o wbox.o PRGNAME = wbox all: wbox wbox: $(OBJ) $(CC) -o $(PRGNAME) $(CCOPT) $(DEBUG) $(OBJ) .c.o: $(CC) -c $(CCOPT) $(DEBUG) $(COMPILE_TIME) $< clean: rm -rf $(PRGNAME) *.o dep: $(CC) -MM *.c wbox-5/README0000644000175000017500000000006210632100420011375 0ustar abbaabbaFor more information visit: http://hping.org/wbox wbox-5/wbox.c0000644000175000017500000010765211311424203011660 0ustar abbaabba/* Wbox - HTTP fun tool, wirtten by Salvatore 'antirez' Sanfilippo * Copyright (C) 2006,2007 Salvatore Sanfilippo, antirez@gmail.com * This softare is released under the following BSD license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wbsignal.h" #include "anet.h" #include "sds.h" /* Flags */ #define WBOX_NONE 0 #define WBOX_ACCEPT_COMPR 1 #define WBOX_USE_HEAD 2 #define WBOX_USE_HTTP10 4 /* Exit codes */ #define WBOX_EXIT_SUCCESS 0 #define WBOX_EXIT_BADARGS 1 #define WBOX_EXIT_RESOLV 2 #define WBOX_EXIT_CONN 3 #define WBOX_EXIT_IO 4 #define WBOX_EXIT_EOF 5 #define WBOX_EXIT_PROTO 6 /* Useful defines */ #define WBOX_NOTUSED(V) ((void) V) /* Hardcoded stuff */ #define WBOX_VERSION 5 #define WBOX_DEFAULT_SERVER_PORT 8081 #define WBOX_DEFAULT_MAX_CLIENTS 20 #define WBOX_RECV_BUF (1024*4) #define WBOX_TIMESPLIT_SAMPLES 40 #define WBOX_COOKIES_MAX 20 /* the ANSI sequence to clear the current line * and move the curosr on the left */ #define WBOX_ANSI_CLEARLINE "\033[1K\033[G" static char *htmlheader = "WBox" ""; static char *htmlfooter= ""; /* Data structures */ typedef struct cookie { char *name; char *value; } cookie; typedef struct wconfig { /* Configuration */ char *url; char *host; char *webroot; char *referer; int dump; int compr; int head; int showhdr; int wait; int clients; int silent; int timesplit; int maxreq; int http10; int close; int cookies; /* number of set cookies */ cookie cookie[WBOX_COOKIES_MAX]; /* Server mode configuration */ int servermode; int serverport; int maxclients; /* Runtime state (client mode) */ int mintime, maxtime; double timesum; /* average = timesum/timesum_samples */ int timesum_samples; /* Runtime state (server mode) */ volatile sig_atomic_t activeclients; } wconfig; typedef struct timesplit { int time; int firstbyte; int lastbyte; } timesplit; /* Reply info describes the HTTP reply we get from server */ typedef struct replyinfo { int code; char *reason; int replylen; int time; int compr; int tsamples; /* timesplit samples used */ timesplit tsample[WBOX_TIMESPLIT_SAMPLES]; } replyinfo; /* Url info describes an URL */ typedef struct urlinfo { char *proto; char *domain; int port; char *req; } urlinfo; /* Request info describes an HTTP request (for server mode) */ #define WBOX_REQ_METHOD_GET 0 #define WBOX_REQ_METHOD_POST 1 #define WBOX_REQ_METHOD_HEAD 2 #define WBOX_REQ_METHOD_OTHER 3 typedef struct reqinfo { int method; int protover; char *file; } reqinfo; /* Global vars */ wconfig conf; /* ---------------------------- support functions --------------------------- */ long long milliseconds(void) { struct timeval tmptv; gettimeofday(&tmptv, NULL); return ((long long)tmptv.tv_sec*1000)+(tmptv.tv_usec/1000); } int strisnumber(char *s) { while(*s == ' ' || (*s >= '0' && *s <= '9')) s++; return *s == '\0'; } static void setlowercase(char *p) { while(*p) { *p = tolower(*p); p++; } } /* string compare, case insensitive */ int strcmpNC(char *s1, char *s2) { int l1 = strlen(s1); int l2 = strlen(s2); while(l1 && l2) { if (tolower((int)*s1) != tolower((int)*s2)) return tolower((int)*s1)-tolower((int)*s2); s1++; s2++; l1--; l2--; } if (!l1 && !l2) return 0; return l1-l2; } /* sdscat() with plain old url encoding */ static char *sdscaturlencode(char *d, char *s) { int len = strlen(s); int i; for(i = 0; i < len; i++) { int c = s[i]; if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) { d = sdscatlen(d,s+i,1); } else if (c == ' ') { d = sdscatlen(d,"+",1); } else if (c == '\n') { d = sdscatlen(d,"%0d%0a",6); } else { char *hexset = "0123456789abcdef"; unsigned int t; t = (unsigned) c; d = sdscatlen(d,"%",1); d = sdscatlen(d,hexset+((t & 0xF0) >> 4),1); d = sdscatlen(d,hexset+((t & 0x0F) >> 4),1); } } return d; } void parseUrl(char *url, urlinfo *ui) { char *copy = sdsnew(url), *p, *d; /* protocol */ p = strstr(copy,"://"); if (!p) { ui->proto = sdsnew("http"); p = copy; } else { *p='\0'; ui->proto = sdsnew(copy); *p=':'; p += 3; /* skip "://" */ } /* domain */ d = strchr(p,'/'); if (!d) d = strchr(p,'?'); if (!d) { ui->domain = sdsnew(p); d = "\0"; } else { char saved = *d; *d = '\0'; ui->domain = sdsnew(p); *d = saved; if (*d == '/') d++; /* skip "/" */ } /* port */ p = strchr(ui->domain,':'); if (!p) { ui->port = 80; } else { *p = '\0'; ui->port = atoi(p+1); if (ui->port == 0) ui->port = 80; } /* request */ ui->req = sdsnew("/"); ui->req = sdscat(ui->req,d); sdsfree(copy); } void freeUrl(urlinfo *ui) { if (ui->proto) sdsfree(ui->proto); if (ui->domain) sdsfree(ui->domain); if (ui->req) sdsfree(ui->req); } /* --------------------------------- HTTP client ---------------------------- */ static char *createHttpReq(urlinfo *ui, int flags, cookie *cookie, int numcookies, char *referer) { char *r = sdsnew((flags&WBOX_USE_HEAD) ? "HEAD ":"GET "); int j; r = sdscat(r, ui->req); r = sdscat(r, " HTTP/1."); r = sdscat(r, (flags & WBOX_USE_HTTP10) ? "0" : "1"); r = sdscat(r, "\r\nHost: "); r = sdscat(r, ui->domain); if (ui->port != 80) { r = sdscatprintf(r,":%d",ui->port); } r = sdscat(r,"\r\n" "User-Agent: Mozilla/5.0 Windows; U; Windows NT 5.1; en-US; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4\r\n" "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n" "Accept-Language: en-us,en;q=0.5\r\n"); if (flags & WBOX_ACCEPT_COMPR) r = sdscat(r,"Accept-Encoding: gzip,deflate\r\n"); r = sdscat(r, "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" "Connection: close\r\n"); for (j = 0; j < numcookies; j++) { if (j == 0) r = sdscat(r,"Cookie: "); r = sdscaturlencode(r,cookie[j].name); r = sdscatlen(r,"=",1); r = sdscaturlencode(r,cookie[j].value); if (j+1 != numcookies) r = sdscatlen(r,"; ",2); else r = sdscatlen(r,"\r\n",2); } if (referer != NULL) { r = sdscat(r,"Referer: "); r = sdscat(r,referer); r = sdscatlen(r,"\r\n",2); } r = sdscat(r,"\r\n"); return r; } static int extractReplyInfo(replyinfo *ri, char *buf, int buflen, wconfig *wc) { char *hdr = sdsnewlen(buf,buflen); /* make a copy to play with it */ char *code,*reason,*p; /* Make sure we don't go over the header */ p = strstr(hdr,"\r\n\r\n"); if (p) *p = '\0'; if (wc->showhdr) { if (!wc->silent) printf("\n"); printf("%s\n",hdr); if (!wc->silent) printf("\n"); } ri->compr = strstr(hdr,"Content-Encoding: gzip") != NULL; /* Some ugly parsing required... */ code = strchr(hdr,' '); if (!code) goto fmterr; code++; reason = strchr(code,' '); if (!reason) goto fmterr; *reason = '\0'; reason++; p = strchr(reason,'\n'); if (!p) goto fmterr; *p = '\0'; p = strchr(reason,'\r'); if (p) *p = '\0'; /* We can now fill the struct */ ri->code = atoi(code); ri->reason = sdsnew(reason); sdsfree(hdr); return 0; fmterr: sdsfree(hdr); return 1; } void initReplyInfo(replyinfo *ri) { ri->code = 0; ri->replylen = 0; ri->time = 0; ri->compr = 0; ri->tsamples = 0; ri->reason = NULL; } void freeReplyInfo(replyinfo *ri) { if (ri->reason) sdsfree(ri->reason); } void copyReplyInfo(replyinfo *d, replyinfo *s) { *d = *s; if (d->reason) d->reason = sdsdup(s->reason); } static int httpRequest(replyinfo *ri, char *ip, urlinfo *ui, wconfig *conf) { char err[ANET_ERR_LEN]; char *req; int s, nwritten, totlen, reqflags = WBOX_NONE; long long stime = milliseconds(); long long tsample_stime = milliseconds(); long long stime_bps = 0; /* used for accurate bandwidth measuring */ initReplyInfo(ri); /* Connect */ s = anetTcpConnect(err, ip, ui->port); if (s == ANET_ERR) { fprintf(stderr, "Opening the connection: %s\n", err); exit(WBOX_EXIT_CONN); } /* Write the HTTP request */ if (conf->compr) reqflags |= WBOX_ACCEPT_COMPR; if (conf->head) reqflags |= WBOX_USE_HEAD; if (conf->http10) reqflags |= WBOX_USE_HTTP10; req = createHttpReq(ui,reqflags,conf->cookie,conf->cookies,conf->referer); nwritten = write(s, req, sdslen(req)); if (nwritten == -1) { perror("Sending the HTTP request"); sdsfree(req); exit(WBOX_EXIT_IO); } /* Read the request */ totlen = 0; while(1) { char buf[WBOX_RECV_BUF]; int nread; nread = anetRead(s, buf, WBOX_RECV_BUF); if (nread == 0) break; if (nread == -1) { perror("Reading from socket"); sdsfree(req); exit(WBOX_EXIT_IO); } /* Populare tsamples */ if (conf->timesplit) { int lastsample = ri->tsamples == WBOX_TIMESPLIT_SAMPLES; timesplit *ts; if (lastsample) ts = &ri->tsample[WBOX_TIMESPLIT_SAMPLES-1]; else ts = &ri->tsample[ri->tsamples]; ts->time = (lastsample ? ts->time : 0) + (int) (milliseconds()-tsample_stime); if (!lastsample) ts->firstbyte = totlen; ts->lastbyte = totlen+nread-1; if (!lastsample) ri->tsamples++; tsample_stime = milliseconds(); } /* Get HTTP reply header information from the first chunk of data */ if (totlen == 0) extractReplyInfo(ri,buf,nread,conf); if (conf->dump) { int lastsample = ri->tsamples == WBOX_TIMESPLIT_SAMPLES; fwrite(buf,nread,1,stdout); if (!lastsample && conf->timesplit) { int idx = ri->tsamples-1; printf("\n\n-----------------------------------------------\n"); printf("CHUNK TIME INFORMATION: %d-%d -> %d ms\n", ri->tsample[idx].firstbyte, ri->tsample[idx].lastbyte, ri->tsample[idx].time); printf("-----------------------------------------------\n\n"); } fflush(stdout); } totlen += nread; if (!conf->dump && !conf->silent && conf->clients <= 1) { printf(WBOX_ANSI_CLEARLINE); printf("%d bytes readed",totlen); if (totlen == WBOX_RECV_BUF*4) stime_bps = milliseconds(); if (totlen > WBOX_RECV_BUF*4) { int recvbytes = totlen-WBOX_RECV_BUF*4; int elapsed = (int) (milliseconds()-stime_bps); float kbs = ((float)recvbytes*1000/elapsed)/1024; printf(" (%.2f kbytes/s)",kbs); } fflush(stdout); } if (conf->close && totlen == nread) break; } /* Done, close the socket and calculate timings */ close(s); sdsfree(req); ri->time = (int) (milliseconds()-stime); ri->replylen = totlen; return 0; } /* return 0 for the parent, 1 for the child */ static int spawnChilds(int count) { int j; for(j = 0; j < count; j++) { pid_t p = fork(); if (p == -1) { perror("fork"); return 0; } if (p == 0) return 1; } return 0; } static void printTimesplit(replyinfo *ri) { int j; for (j = 0; j < ri->tsamples; j++) { printf(" [%d] %d-%d -> %d ms\n", j, ri->tsample[j].firstbyte, ri->tsample[j].lastbyte, ri->tsample[j].time); } } /* --------------------------------- HTTP server ---------------------------- */ static int parseRequest(char *req, reqinfo *ri) { char *copy, *r; char *p; int noproto = 0; ri->file = NULL; /* Make it safe to free later */ copy = r = sdsdup(req); /* work with a copy */ p = strchr(r,' '); if (!p) goto fmterr; *p = '\0'; setlowercase(r); if (!strcmp(r,"get")) ri->method = WBOX_REQ_METHOD_GET; else if (!strcmp(r,"post")) ri->method = WBOX_REQ_METHOD_POST; else if (!strcmp(r,"head")) ri->method = WBOX_REQ_METHOD_HEAD; else ri->method = WBOX_REQ_METHOD_OTHER; r = p+1; while(*r == ' ') r++; p = NULL; (p = strchr(r,' ')) || (p = strchr(r,'\r')) || (p = strchr(r,'\n')); if (p) *p = '\0'; else noproto = 1; if (r[0] != '/') goto fmterr; ri->file = sdsnew(r); ri->protover = 10; if (!noproto) { r = p+1; if ((p = strchr(r,'\n')) != NULL) { *p = '\0'; if (strstr(r, "HTTP/1.1")) ri->protover = 11; } } sdsfree(copy); return 0; fmterr: sdsfree(copy); return 1; } static void freeReqInfo(reqinfo *ri) { if (ri->file) sdsfree(ri->file); } static void urldecode(char *d, char *s, int n) { char *start = d; if (n < 1) return; while(*s && n > 1) { int c = *s; switch(c) { case '+': c = ' '; break; case '%': if (*(s+1) && *(s+2)) { int high = toupper(*(s+1)); int low = toupper(*(s+2)); if (high <= '9') high -= '0'; else high = (high - 'A') + 10; if (low <= '9') low -= '0'; else low = (low - 'A') + 10; c = (high << 4)+low; s += 2; } break; } if (c != ' ' || d != start) { *d++ = c; n--; } s++; } /* Right trim */ *d = '\0'; d--; while (d >= start && *d == ' ') { *d = '\0'; d--; } } static char *createFullPath(char *root, char *file) { char *uefile, *fp, *p; int len; uefile = sdsnewlen(NULL,sdslen(file)+1); urldecode(uefile,file,sdslen(uefile)); /* url decode the original name */ sdsupdatelen(uefile); fp = sdsnew(root); fp = sdscat(fp,uefile); sdsfree(uefile); while((p=strstr(fp,".."))) { *p = '_'; *(p+1) = '_'; } /* Make sure there aren't trailing slashes */ len = sdslen(fp)-1; while(len >= 1 && fp[len] == '/') fp[len] = '\0'; sdsupdatelen(fp); return fp; } static char *createHttpReply(reqinfo *ri, char *code, char *reason, char *ctype, int len) { char date[128]; struct tm *tm; time_t t; char *r; r = sdsnew("HTTP/1."); r = sdscat(r,ri->protover == 11 ? "1 " :"0 "); r = sdscat(r,code); r = sdscat(r," "); r = sdscat(r,reason); r = sdscat(r,"\r\n"); t = time(NULL); tm = gmtime(&t); strftime(date, 128, "%a, %d %b %Y %H:%M:%S GMT", tm); r = sdscat(r,"Date: "); r = sdscat(r,date); r = sdscatprintf(r, "\r\nServer: WBox %d (http://hping.org/wbox)\r\n", WBOX_VERSION); r = sdscat(r,"Content-type: "); r = sdscat(r,ctype); if (len != -1) r = sdscatprintf(r,"\r\nContent-Length: %d",len); r = sdscat(r,"\r\n\r\n"); return r; } static char *sdscatentities(char *d, char *s) { int len = strlen(s); int i; for(i = 0; i < len; i++) { switch(s[i]) { case '<': d=sdscatlen(d,"<",4); break; case '>': d=sdscatlen(d,">",4); break; case '&': d=sdscatlen(d,"&",5); break; case '"': d=sdscatlen(d,""",6); break; default: d=sdscatlen(d,s+i,1); break; } } return d; } /* Special version of sdscat() that takes care of special * chars in filenames having a special meaning for the * browser (like #). This function also performs HTML entities * conversion. */ static char *sdscaturl(char *d, char *s) { char *hexset = "0123456789abcdef"; int len; int i; unsigned int t; /* Entities conversion step */ { char *new = sdsnew(""); new = sdscatentities(new,s); s = new; } len = strlen(s); for(i = 0; i < len; i++) { switch(s[i]) { case '#': t = (unsigned) s[i]; d = sdscatlen(d,"%",1); d = sdscatlen(d,hexset+((t & 0xF0) >> 4),1); d = sdscatlen(d,hexset+((t & 0x0F) >> 4),1); break; default: d = sdscatlen(d,s+i,1); break; } } sdsfree(s); return d; } static char *createDirListing(char *path, char *webpath) { DIR *d; struct dirent *de; struct stat sbuf; char *b; int onlydirs; d = opendir(path); if (d == NULL) return sdsnew("Can't open the directory..."); b = sdsnew(htmlheader); b = sdscat(b,"

Index of "); b = sdscatentities(b,webpath); b = sdscat(b,"

\n"); for (onlydirs = 1; onlydirs >= 0; onlydirs--) { while((de = readdir(d))) { char *filepath, *trname; int retval, isdir; char unitchar; float size; filepath = sdsdup(path); filepath = sdscatlen(filepath,"/",1); filepath = sdscat(filepath,de->d_name); retval = stat(filepath,&sbuf); if (retval == -1) { sdsfree(filepath); continue; } if (access(filepath,R_OK) == -1) { sdsfree(filepath); continue; } sdsfree(filepath); isdir = S_ISDIR(sbuf.st_mode); if (onlydirs != isdir) continue; if (isdir && de->d_name[0] == '.' && de->d_name[1] == '\0') continue; /* File name */ b = sdscat(b,"\n"); sdsfree(trname); /* File size */ size = sbuf.st_size; if (size > 1024*1024*1024) { size /= 1024*1024*1024; unitchar='G'; } else if (size > 1024*1024) { size /= 1024*1024; unitchar='M'; } else if (size > 1024) { size /= 1024; unitchar='k'; } else { unitchar='b'; } b = sdscat(b,"\n"); } rewinddir(d); } closedir(d); b = sdscat(b,"
d_name); if (sdslen(trname) > 30) { trname = sdsrange(trname,0,30); trname = sdscat(trname,"...>"); } b = sdscaturl(b,de->d_name); if (isdir) { b = sdscatlen(b,"/",1); b = sdscat(b,"\" class=\"dir\">[DIR] "); } else b = sdscat(b,"\">"); b = sdscatentities(b,trname); b = sdscat(b,""); if (size-(int)size) b = sdscatprintf(b,"%.1f%c",size,unitchar); else b = sdscatprintf(b,"%d%c",(int)size,unitchar); b = sdscat(b,"
\n"); b = sdscat(b,htmlfooter); return b; } static char *guessContentType(char *filename) { char *p; p = strrchr(filename,'.'); if (!p) return "text/plain"; p++; if (!strcmpNC(p,"jpg") || !strcmpNC(p,"jpeg")) return "image/jpeg"; if (!strcmpNC(p,"css")) return "text/css"; if (!strcmpNC(p,"js")) return "text/javascript"; if (!strcmpNC(p,"ico")) return "image/x-icon"; if (!strcmpNC(p,"png")) return "image/png"; if (!strcmpNC(p,"gif")) return "image/gif"; if (!strcmpNC(p,"html")) return "text/html"; if (!strcmpNC(p,"htm")) return "text/html"; if (!strcmpNC(p,"xml")) return "text/plain"; if (!strcmpNC(p,"txt")) return "text/plain"; if (!strcmpNC(p,"c")) return "text/plain"; if (!strcmpNC(p,"rb")) return "text/plain"; if (!strcmpNC(p,"py")) return "text/plain"; if (!strcmpNC(p,"cpp")) return "text/plain"; if (!strcmpNC(p,"c++")) return "text/plain"; if (!strcmpNC(p,"tcl")) return "text/plain"; if (!strcmpNC(p,"pl")) return "text/plain"; if (!strcmpNC(p,"lua")) return "text/plain"; if (!strcmpNC(p,"csv")) return "text/plain"; if (!strcmpNC(p,"pdf")) return "application/pdf"; if (!strcmpNC(p,"mp3")) return "audio/mpeg"; if (!strcmpNC(p,"mpg")) return "video/mpeg"; /* All the rest is binary data... */ return "text/plain"; } static void serverModeChild(int s, char *ip, int port, wconfig *conf) { char *req = sdsnew(""); char buf[WBOX_RECV_BUF]; char *fullpath, *reply = NULL; char *document = NULL; struct stat sbuf; reqinfo ri; /* Read the request */ while(1) { int nread, len; nread = read(s,buf,WBOX_RECV_BUF); if (nread == 0) { printf("%s:%d EOF from client\n",ip,port); exit(WBOX_EXIT_IO); } else if (nread == -1) { printf("%s:%d reading: %s\n", ip, port, strerror(errno)); exit(WBOX_EXIT_IO); } req = sdscatlen(req,buf,nread); len = sdslen(req); if (len > 4) { if (!memcmp(req+(len-2),"\n\n",2) || !memcmp(req+(len-4),"\r\n\r\n",4)) break; } } /* Parse it */ if (parseRequest(req,&ri)) { printf("%s:%d bad request:\n%s\n", ip, port, req); sdsfree(req); exit(WBOX_EXIT_PROTO); } sdsfree(req); /* Create the full path of the resource */ fullpath = createFullPath(conf->webroot, ri.file); /* printf("Method: %d, file: %s, protover: %d\n", ri.method, fullpath, ri.protover); */ /* Generate the reply */ if (access(fullpath,R_OK) == -1) { /* 404 */ if (!conf->silent) printf("%s:%d 404: %s\n", ip, port, fullpath); reply = createHttpReply(&ri,"404","Not Found","text/html",-1); reply = sdscat(reply, htmlheader); reply = sdscat(reply, "

404 Not Found

"); reply = sdscatprintf(reply, "

"); reply = sdscatentities(reply,ri.file); reply = sdscat(reply," was not found on this server

"); reply = sdscat(reply, htmlfooter); anetWrite(s,reply,sdslen(reply)); goto cleanup; } if (stat(fullpath,&sbuf) == -1) goto cleanup; if (S_ISDIR(sbuf.st_mode)) { /* Directory listing */ document = createDirListing(fullpath,ri.file); reply = createHttpReply(&ri,"200","OK","text/html",sdslen(document)); anetWrite(s,reply,sdslen(reply)); anetWrite(s,document,sdslen(document)); goto cleanup; } else { /* Regular file */ char *ctype = guessContentType(ri.file); char buf[WBOX_RECV_BUF]; int fd; reply = createHttpReply(&ri,"200","OK",ctype,sbuf.st_size); anetWrite(s,reply,sdslen(reply)); if ((fd = open(fullpath,O_RDONLY)) == -1) goto cleanup; while(1) { int nread; nread = read(fd,buf,WBOX_RECV_BUF); if (nread == 0 || nread == -1) break; if (anetWrite(s,buf,nread) == -1) { if (!conf->silent) printf("%s:%d write error\n", ip, port); break; } } } cleanup: sdsfree(document); sdsfree(reply); sdsfree(fullpath); freeReqInfo(&ri); close(s); if (!conf->silent) printf("%s:%d served with success\n", ip, port); } static void serverMode(wconfig *conf) { int server, clientport; char err[ANET_ERR_LEN], clientip[32]; if (!conf->webroot) { fprintf(stderr, "Sorry, you must specify 'webroot ' in server mode.\n"); exit(WBOX_EXIT_BADARGS); } /* Make sure the webroot does not end with a slash */ { int wblen = strlen(conf->webroot); while (wblen && conf->webroot[wblen-1] == '/') { conf->webroot[wblen-1] = '\0'; wblen--; } } printf("WBOX starting in server mode, port %d, webroot %s\n", conf->serverport, conf->webroot); /* Configure the TCP server */ server = anetTcpServer(err,conf->serverport,NULL); if (server == ANET_ERR) { fprintf(stderr, "Starting in server mode (port %d): %s\n", conf->serverport, err); exit(WBOX_EXIT_IO); } while(1) { pid_t pid; int fd = anetAccept(err, server, clientip, &clientport); if (fd == ANET_ERR) { fprintf(stderr, "Warning, accepting client: %s\n", err); continue; } if (conf->activeclients == conf->maxclients) { printf("%s:%d closing connection! max number of clients reached (tune this using the maxclients option)\n",clientip,clientport); close(fd); continue; } if (!conf->silent) printf("%s:%d connected\n",clientip,clientport); conf->activeclients++; pid = fork(); if (pid == -1) { conf->activeclients--; perror("fork"); close(fd); continue; } if (pid == 0) { close(server); serverModeChild(fd,clientip,clientport,conf); exit(WBOX_EXIT_SUCCESS); } else { if (!conf->silent) printf("%s:%d handled by process %d\n",clientip,clientport,pid); close(fd); } } } /* --------------------------------- Main() & co ---------------------------- */ static void wboxHelp(void) { printf( "Usage: wbox [options ...]\n\n" "options:\n\n" " - stop after requests\n" "compr - send Accept-Encoding: gzip,deflate in request\n" "showhdr - show the HTTP reply header\n" "dump - show the HTTP reply header + body\n" "silent - don't show status lines\n" "head - use the HEAD method instead of GET\n" "http10 - use HTTP/1.0 instead of HTTP/1.1\n" "close - close the connection after reading few bytes\n" "host - use as Host: field in HTTP request\n" "timesplit - show transfer times for different data chunks\n" "wait - wait seconds between requests. Default 1.\n" "clients - spawn concurrent clients (via fork()).\n" "referer - Send the specified referer header.\n" "cookie - Set cookie name=val, can be used multiple times.\n" "-h or --help - show this help.\n" "-v - show version.\n" "\nSERVER MODE\n\n" "Usage: wbox servermode webroot [serverport (def 8081)]\n\n" "options:\n\n" "maxclients - Max concurrent clients in server mode (default 20).\n" "\nEXAMPLES\n\n" "wbox wikipedia.org (simplest, basic usage)\n" "wbox wikipedia.org 3 compr wait 0 (three requests, compression, no delay)\n" "wbox wikipedia.org 1 showhdr silent (just show the HTTP reply header)\n" "wbox wikipedia.org timesplit (show splitted time information)\n" "wbox 1.2.3.4 host example.domain (test a virtual domain at 1.2.3.4)\n" "wbox servermode webroot /tmp/mydocuments (Try it with http://127.0.0.1:8081)\n" "\n" "More docs? there is a tutorial at http://hping.org/wbox\n" ); } static void printStats(void) { printf("--- %d replies received",conf.timesum_samples); if (conf.timesum_samples) { printf(", time min/avg/max = %d/%.2f/%d", conf.mintime, (float)(conf.timesum/conf.timesum_samples), conf.maxtime); } printf(" ---\n"); } static void sigHandler(int signum) { if (signum == SIGINT) { WBOX_NOTUSED(signum); if (!conf.silent) { printf("\n"); printStats(); printf("\n"); } exit(WBOX_EXIT_SUCCESS); } else if (signum == SIGCHLD) { int status; while (waitpid(-1,&status,WNOHANG) > 0) conf.activeclients--; if (!conf.silent && conf.servermode) printf("%d active clients\n", conf.activeclients); } } static void parseArgs(char **argv, int argc, wconfig *conf) { int j; memset(conf,0,sizeof(*conf)); conf->wait = 1; conf->maxreq = -1; conf->serverport = WBOX_DEFAULT_SERVER_PORT; conf->maxclients = WBOX_DEFAULT_MAX_CLIENTS; if (argc < 2) { wboxHelp(); exit(WBOX_EXIT_BADARGS); } if (!strcmp(argv[1],"-h") || !strcmp(argv[1],"--help")) { wboxHelp(); exit(WBOX_EXIT_SUCCESS); } if (!strcmp(argv[1],"-v")) { printf("WBox version %d\n", WBOX_VERSION); exit(WBOX_EXIT_SUCCESS); } /* Server mode option must be at argv[1] ... */ if (!strcmp(argv[1],"servermode")) conf->servermode = 1; /* Make sure it's possible to call wbox with every kind * of argument as url including "-h", using: * * wbox -- -h * * Hardly useful but there must be a way to do this for * the user. */ conf->url = argv[1]; if (argc > 2 && !strcmp(argv[1],"--")) { argv++; argc--; conf->url = argv[1]; } /* Option parsing, in a more discorsive way compared to * the usual unix switches */ for(j = 2; j < argc; j++) { int next = (j+1 != argc); int leftargs = argc-(j+1); if (!strcmp(argv[j],"dump")) { conf->dump=1; } else if (!strcmp(argv[j],"compr")) { conf->compr=1; } else if (!strcmp(argv[j],"head")) { conf->head=1; } else if (!strcmp(argv[j],"timesplit")) { conf->timesplit=1; } else if (!strcmp(argv[j],"showhdr")) { conf->showhdr=1; } else if (!strcmp(argv[j],"silent")) { conf->silent=1; } else if (!strcmp(argv[j],"http10")) { conf->http10=1; } else if (!strcmp(argv[j],"close")) { conf->close=1; } else if (next && !strcmp(argv[j],"host")) { j++; conf->host = argv[j]; } else if (next && !strcmp(argv[j],"wait")) { j++; conf->wait = atoi(argv[j]); } else if (next && !strcmp(argv[j],"clients")) { j++; conf->clients = atoi(argv[j]); } else if (next && !strcmp(argv[j],"referer")) { j++; conf->referer = argv[j]; } else if (leftargs >= 2 && !strcmp(argv[j],"cookie")) { if (conf->cookies < WBOX_COOKIES_MAX) { conf->cookie[conf->cookies].name = argv[j+1]; conf->cookie[conf->cookies].value = argv[j+2]; conf->cookies++; } j += 2; } else if (next && !strcmp(argv[j],"webroot")) { j++; conf->webroot = argv[j]; } else if (next && !strcmp(argv[j],"serverport")) { j++; conf->serverport = atoi(argv[j]); } else if (next && !strcmp(argv[j],"maxclients")) { j++; conf->maxclients = atoi(argv[j]); } else if (!strcmp(argv[j],"-h") || !strcmp(argv[j],"--help")) { wboxHelp(); exit(WBOX_EXIT_SUCCESS); } else if (strisnumber(argv[j]) && atoi(argv[j]) > 0) { conf->maxreq = atoi(argv[j]); } else { fprintf(stderr, "\n * Wrong option or params: %s\n\n", argv[j]); wboxHelp(); exit(WBOX_EXIT_BADARGS); } } } static void printReplyStatus(int reqid, replyinfo *oldri, replyinfo *ri) { /* Print status line */ printf(WBOX_ANSI_CLEARLINE); /* hostname id */ printf("%d. %d %s",reqid,ri->code,ri->reason ? ri->reason : "()"); /* reply length */ if (reqid != 0 && oldri->replylen != ri->replylen) printf(" (%d)",ri->replylen); else printf(" %d",ri->replylen); printf(" bytes"); /* request time */ printf(" %d ms",ri->time); if (ri->compr) printf(" compr"); printf("\n"); } int main(int argc, char **argv) { char err[ANET_ERR_LEN]; char ip[32]; int reqid = 0; replyinfo ri, oldri; urlinfo ui; setlocale(LC_ALL,"C"); Signal(SIGCHLD,sigHandler); Signal(SIGPIPE,sigHandler); parseArgs(argv,argc,&conf); /* Start in server mode if needed */ if (conf.servermode) serverMode(&conf); parseUrl(conf.url,&ui); if (anetResolve(err,ui.domain,ip) != ANET_OK) { fprintf(stderr,"%s\n",err); exit(WBOX_EXIT_RESOLV); } if (conf.host != NULL) { sdsfree(ui.domain); ui.domain=sdsnew(conf.host); } if (!conf.silent) { printf("WBOX %s (%s) port %d",ui.domain,ip,ui.port); if (conf.compr) printf(" [compr]"); if (conf.head) printf(" [head]"); if (conf.wait != 1) printf(" [wait %d]",conf.wait); printf("\n"); } if (conf.clients > 1) { if (spawnChilds(conf.clients-1)) { /* Childs specific code */ } else { /* Parent specific code */ Signal(SIGINT,sigHandler); } } else { Signal(SIGINT,sigHandler); } initReplyInfo(&oldri); while(1) { int flags = WBOX_NONE; if (conf.compr) flags |= WBOX_ACCEPT_COMPR; if (conf.head) flags |= WBOX_USE_HEAD; /* Request */ httpRequest(&ri,ip,&ui,&conf); conf.timesum += ri.time; conf.timesum_samples++; if (reqid == 0) { conf.mintime = conf.maxtime = ri.time; } else { if (conf.mintime > ri.time) conf.mintime = ri.time; if (conf.maxtime < ri.time) conf.maxtime = ri.time; } if (!conf.silent) { printReplyStatus(reqid,&oldri,&ri); if (conf.timesplit) printTimesplit(&ri); } reqid++; freeReplyInfo(&oldri); copyReplyInfo(&oldri,&ri); freeReplyInfo(&ri); if (reqid == conf.maxreq) break; sleep(conf.wait); } freeUrl(&ui); freeReplyInfo(&oldri); if (!conf.silent) printStats(); return 0; } wbox-5/anet.h0000644000175000017500000000405211310136647011636 0ustar abbaabba/* Copyright (C) 2006,2007 Salvatore Sanfilippo, antirez@gmail.com * This softare is released under the following BSD license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ANET_H #define ANET_H #define ANET_OK 0 #define ANET_ERR -1 #define ANET_ERR_LEN 256 int anetNonBlock(char *err, int fd); int anetTcpNoDelay(char *err, int fd); int anetTcpConnect(char *err, char *addr, int port); int anetRead(int fd, void *buf, int count); int anetResolve(char *err, char *host, char *ipbuf); int anetTcpServer(char *err, int port, char *bindaddr); int anetAccept(char *err, int serversock, char *ip, int *port); int anetWrite(int fd, void *buf, int count); #endif