/* darkstat: a network traffic analyzer * (c) 2001-2003, Emil Mikulic. */ /* darkstat's own mini-httpd! colours by cor ;o) */ #include "darkstat.h" #include "www.h" #include "dns.h" #include "host_db.h" #include "port_db.h" #include "proto.h" #include "graph.h" #include "gif.h" #include "content.h" #include "acct.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define POLLDELAY 2 /* seconds */ #define SCOREBOARD 10 static int refresh; /* saves me passing it around all the time */ static char cellbg_1[] = "\"#99CCFF\""; static char cellbg_2[] = "\"#6699FF\""; static void html_graph(content *cnt, const char *title, const int GRAPHWIDTH, const int GRAPHHEIGHT, const int64 graph_in[], const int64 graph_out[], const int pos, const int GRAPH_SIZE) { #define _IN "\"#3333CC\"" #define _I_R 0x33 #define _I_G 0x33 #define _I_B 0xCC #define _OUT "\"#6699FF\"" #define _O_R 0x66 #define _O_G 0x99 #define _O_B 0xFF /* SAFE_GRAPHS causes the graph data to be copied to a temporary local * array first. The downside is a small performance hit, the upside is * no more botched graphs. */ #define SAFE_GRAPHS int i, total_width = 0, total_strips = 0, skip, incr = (graph_in == graph_day_in); /* cheat: mday is g_days+1 */ double divisor; int64 max_transfer; #ifndef HAVE_64PRINT_COMMAS char *maximum; #endif #ifdef SAFE_GRAPHS int64 *local_in, *local_out; local_in = (int64*)malloc(sizeof(int64) * GRAPH_SIZE); local_out = (int64*)malloc(sizeof(int64) * GRAPH_SIZE); if (!local_in || !local_out) freakout("Ran out of memory in html_graph()"); memcpy(local_in, graph_in, sizeof(int64) * GRAPH_SIZE); memcpy(local_out, graph_out, sizeof(int64) * GRAPH_SIZE); #define graph_in local_in #define graph_out local_out #endif SET64(max_transfer, 0,0); /* find maximum value for this graph */ for (i=0; i" "" "" "" "
" "%s " "(%s %s " "%s)
" "%s %'Lu %s
" "", title, _("in"), _("and"), _("out"), _("Maximum:"), max_transfer, _("bytes")); #else c_appendf(cnt, "
" "
" "" "" "
" "%s " "(%s %s " "%s)
" "%s %s %s
" "", title, _("in"), _("and"), _("out"), _("Maximum:"), maximum = strint64(max_transfer), _("bytes")); free(maximum); #endif divisor = db_i64div32(max_transfer, GRAPHHEIGHT); if (divisor < 1.0) divisor = 1.0; c_appends(cnt, ""); i = pos; do { int in, out, blank, width; char alt[64]; #ifndef HAVE_64PRINT_COMMAS char *in64, *out64; #endif i++; if (i >= GRAPH_SIZE) i = 0; /* width calculation */ width = (GRAPHWIDTH - total_width - 2*GRAPH_SIZE) / (GRAPH_SIZE - total_strips); total_strips++; total_width += width; /* get data */ in = i_i64divdb(graph_in[i], divisor); out = i_i64divdb(graph_out[i], divisor); blank = GRAPHHEIGHT - in - out; /* construct alt */ #ifndef HAVE_64PRINT_COMMAS snprintf(alt, 64, "%d: %s %s / %s %s", i+incr, in64 = strint64(graph_in[i]), _("in"), out64 = strint64(graph_out[i]), _("out")); free(in64); free(out64); #else snprintf(alt, 64, "%d: %'Lu %s / %'Lu %s", i+incr, graph_in[i], _("in"), graph_out[i], _("out")); #endif /* write it out */ c_appends(cnt, ""); } while (i != pos); c_appends(cnt, ""); /* add an extra table row containing the bar times */ if (GRAPH_SIZE > 31) { /* thin bars */ c_appends(cnt, ""); skip = 3; } else { /* thick bars */ c_appends(cnt, ""); skip = 2; } for (i=0; i= GRAPH_SIZE) c_appends(cnt, ""); else c_appendf(cnt, "", skip, num+incr); } c_appends(cnt, ""); c_appends(cnt, "
"); if (blank) { c_appendf(cnt, "", width, blank); } if (in) c_appendf(cnt, "%s", (blank)?"
":"", width, in, alt, alt); if (out) c_appendf(cnt, "%s", (in||blank)?"
":"", width, out, alt, alt); c_appends(cnt, "
" " " "%d" "
" "
"); #ifdef SAFE_GRAPHS #undef graph_in #undef graph_out free(local_in); free(local_out); #endif } static const char css[] = ""; static char *linkbox = NULL; static void init_linkbox(void) { char buf[1024]; sprintf(buf, "" "" "
" " " PACKAGE " v" VERSION " " "" "" "
" "%s | " "%s | " "%s | " "%s | " "%s | " "%s" "
" "
", _("main"), _("hosts"), _("ports"), _("protocols"), _("graphs"), _("homepage") ); linkbox = strdup(buf); if (!linkbox) freakout("WWW: ran out of memory for linkbox"); } /* refresh idea due to Danny Brewer - thank you */ static void refresh_header(content *cnt, const char *title) { if (refresh) c_appendf(cnt, "darkstat : %s" "" "%s%s
", title, refresh, css, linkbox); else c_appendf(cnt, "darkstat : %s" "%s%s
", title, css, linkbox); } static void refresh_footer(content *cnt) { if (refresh == 0) { c_appends(cnt, "
"); c_appendf(cnt, _("Want this page refreshed every %s seconds?"), ""); c_appendf(cnt, "
" "", _("Yes")); } else { char buf[70]; snprintf(buf, 70, "", refresh); c_appends(cnt, "
"); c_appendf(cnt, _("This page is being refreshed " "every %s seconds."), buf); c_appendf(cnt, " " "" "
", _("Change"), _("Stop")); } } static content *www_frontpage(void) { #ifndef HAVE_64PRINT_COMMAS char *x_packets, *x_data, *x_hosts; #endif content *cnt = c_new(); time_t uptime = t_already + (time(NULL) - t_start); dword days, hours, minutes; refresh_header(cnt, _("main")); c_appends(cnt, ""); #ifdef HAVE_64PRINT_COMMAS c_appendf(cnt, "" "" "" "" "" "", _("Packets captured:"), num_packets, _("Data off the wire:"), total_data, _("bytes"), _("Unique hosts:"), host_db_used() ); #else c_appendf(cnt, "" "" "" "" "" "" , _("Packets captured:"), x_packets = strint64(num_packets), _("Data off the wire:"), x_data = strint64(total_data), _("bytes"), _("Unique hosts:"), x_hosts = strint32(host_db_used()) ); free(x_packets); free(x_data); free(x_hosts); #endif /* uptime */ #define S_MIN 60 #define S_HR (S_MIN*60) #define S_DAY (S_HR*24) days = uptime / S_DAY; uptime -= days * S_DAY; hours = uptime / S_HR; uptime -= hours * S_HR; minutes = uptime / S_MIN; uptime -= minutes * S_MIN; #undef S_DAY #undef S_HR #undef S_MIN c_appendf(cnt, "" "
%s%'Lu
%s%'Lu %s
%s%'u
%s%s
%s%s %s
%s%s
%s:", _("Counting")); if (days) c_appendf(cnt, "%u %s ", days, _("days")); if (days || hours) c_appendf(cnt, "%u %s ", hours, _("hours")); if (days || hours || minutes) c_appendf(cnt, "%u %s ", minutes, _("minutes")); c_appendf(cnt, "%u %s ", uptime, _("seconds")); /* since */ c_appendf(cnt, "
%s%s

", _("Running since:"), asctime(localtime(&t_start)) ); pthread_mutex_lock(&graph_mutex); graph_calibrate_to_clock(); html_graph(cnt, _("Last 60 seconds"), 400, 200, graph_sec_in, graph_sec_out, g_secs, GRAPH_SECS); pthread_mutex_unlock(&graph_mutex); c_appendf(cnt, "
%s ::[ " "%s%s%s | " "%s%s%s | " "%s%s%s " "]::
", _("DNS resolution is"), (want_resolve==ON)?"":"", _("on"), (want_resolve==ON)?"":"", (want_resolve==OFF)?"":"", _("off"), (want_resolve==OFF)?"":"", (want_resolve==IN_PROGRESS)?"":"", _("cycling once"), (want_resolve==IN_PROGRESS)?"":"" ); refresh_footer(cnt); return cnt; } static content *www_graphs(void) { content *cnt = c_new(); refresh_header(cnt, _("graphs")); c_appends(cnt, "" "
"); pthread_mutex_lock(&graph_mutex); graph_calibrate_to_clock(); html_graph(cnt, _("Last 60 seconds"), 300, 200, graph_sec_in, graph_sec_out, g_secs, GRAPH_SECS); c_appends(cnt, ""); html_graph(cnt, _("Last 60 minutes"), 300, 200, graph_min_in, graph_min_out, g_mins, GRAPH_MINS); c_appends(cnt, "
"); html_graph(cnt, _("Last 24 hours"), 300, 200, graph_hr_in, graph_hr_out, g_hrs, GRAPH_HRS); c_appends(cnt, ""); html_graph(cnt, _("Last 30 days"), 300, 200, graph_day_in, graph_day_out, g_days, GRAPH_DAYS); pthread_mutex_unlock(&graph_mutex); c_appends(cnt, "
"); refresh_footer(cnt); return cnt; } static content *www_hosts(const sort_type st, const int limited) { int i; content *cnt = c_new(); host_record **list; int listend, used = host_db_used(); char *cellbg = cellbg_2; refresh_header(cnt, _("hosts")); pthread_mutex_lock(&db_mutex); if (limited) c_appendf(cnt, _("Hosts (sorted by %s, top %d)"), (st==IN)?_("incoming"):(st==OUT)?_("outgoing"): (st==TOTAL)?_("total"):(st==MAIN)?_("IP"):"ERROR", min(HOST_REPORT_LIMIT, used)); else c_appendf(cnt, _("Hosts (sorted by %s)"), (st==IN)?_("incoming"):(st==OUT)?_("outgoing"): (st==TOTAL)?_("total"):(st==MAIN)?_("IP"):"ERROR"); list = host_db_sort(st); pthread_mutex_unlock(&db_mutex); c_appendf(cnt, "

" "" "
" "" "" "" "" "" "" "", _("IP"), _("full"), _("Hostname"), _("In"), _("full"), _("Out"), _("full"), _("Total"), _("full") ); /* you want the full thing or not? */ if (limited) listend = used-HOST_REPORT_LIMIT; else listend = 0; if (listend < 0) listend = 0; for (i=used-1; i>=listend; i--) { host_record *h; int64 t64; #ifndef HAVE_64PRINT_COMMAS char *data_in, *data_out, *total; #endif cellbg = (cellbg == cellbg_1) ? cellbg_2 : cellbg_1; /* grab the record */ h = list[i]; /* total = in + out */ t64 = h->data_in; i64add64(t64, h->data_out); #ifdef HAVE_64PRINT_COMMAS c_appendf(cnt, "" "" "" "" "" "", cellbg, (h->ip_addr >> 24) & 255, (h->ip_addr >> 16) & 255, (h->ip_addr >> 8) & 255, h->ip_addr & 255, cellbg, h->hostname?h->hostname:" ", cellbg, h->data_in, cellbg, h->data_out, cellbg, h->data_in + h->data_out); #else c_appendf(cnt, "" "" "" "" "" "", cellbg, (h->ip_addr >> 24) & 255, (h->ip_addr >> 16) & 255, (h->ip_addr >> 8) & 255, h->ip_addr & 255, cellbg, h->hostname?h->hostname:" ", cellbg, data_in = strint64(h->data_in), cellbg, data_out = strint64(h->data_out), cellbg, total = strint64(t64)); free(data_in); free(data_out); free(total); #endif } free(list); c_appends(cnt, "
" "%s" " (%s)" "%s" "%s" " (%s)" "" "%s" " (%s)" "" "%s" " (%s)" "
%d.%d.%d.%d%s%'Lu%'Lu%'Lu
%d.%d.%d.%d%s%s%s%s
" "
"); refresh_footer(cnt); return cnt; } static content *www_ports(const sort_type st, const int limited) { content *cnt = c_new(); int i; port_record **list; int listend; char *cellbg = cellbg_2; refresh_header(cnt, _("ports")); pthread_mutex_lock(&db_mutex); if (limited) c_appendf(cnt, _("Ports (TCP, sorted by %s, top %d)"), (st==IN)?_("incoming"):(st==OUT)?_("outgoing"): (st==TOTAL)?_("total"):(st==MAIN)?_("port number"): "ERROR", min(PORT_REPORT_LIMIT, port_db.used)); else c_appendf(cnt, _("Ports (TCP, sorted by %s)"), (st==IN)?_("incoming"):(st==OUT)?_("outgoing"): (st==TOTAL)?_("total"):(st==MAIN)?_("port number"): "ERROR"); c_appendf(cnt, "

" "" "
" "" "" "" "" "" "" "", _("Port"), _("full"), _("In"), _("full"), _("Out"), _("full"), _("Total"), _("full") ); list = port_db_sort(st); /* you want the full thing or not? */ if (limited) listend = port_db.used-PORT_REPORT_LIMIT; else listend = 0; if (listend < 0) listend = 0; for (i=port_db.used-1; i>=listend; i--) { port_record *p; int64 t64; #ifndef HAVE_64PRINT_COMMAS char *data_in, *data_out, *total; #endif const char *name; cellbg = (cellbg == cellbg_1) ? cellbg_2 : cellbg_1; /* grab the record */ p = list[i]; /* total */ t64 = p->data_in; i64add64(t64, p->data_out); name = service_name(p->port); if (!name) name = "(unknown)"; #ifdef HAVE_64PRINT_COMMAS c_appendf(cnt, "" "" "" "" "" "", cellbg,p->port, cellbg,name, cellbg,p->data_in, cellbg,p->data_out, cellbg,t64); #else c_appendf(cnt, "" "" "" "" "" "", cellbg,p->port, cellbg,name, cellbg,data_in = strint64(p->data_in), cellbg,data_out = strint64(p->data_out), cellbg,total = strint64(t64)); free(data_in); free(data_out); free(total); #endif } pthread_mutex_unlock(&db_mutex); free(list); c_appends(cnt, "
" "%s" " (%s)" "" "%s" " (%s)" "" "%s" " (%s)" "" "%s" " (%s)" "
%d%s%'Lu%'Lu%'Lu
%d%s%s%s%s
" "
"); refresh_footer(cnt); return cnt; } static content *www_protocols(void) { content *cnt = c_new(); char *cellbg = cellbg_2; int i; int64 zero; SET64(zero, 0, 0); refresh_header(cnt, _("protocols")); c_appendf(cnt, "" "
" "" "" "" "" "" "", _("Protocol"), _("In"), _("Out"), _("Other"), _("Total")); for (i=0; i<256; i++) if (i64bigger(proto_in[i], zero) || i64bigger(proto_out[i], zero) || i64bigger(proto_other[i], zero)) { int64 t64; #ifndef HAVE_64PRINT_COMMAS char *data_in, *data_out, *data_other, *total; #endif cellbg = (cellbg == cellbg_1) ? cellbg_2 : cellbg_1; /* total */ t64 = proto_in[i]; i64add64(t64, proto_out[i]); i64add64(t64, proto_other[i]); #ifdef HAVE_64PRINT_COMMAS c_appendf(cnt, "" "" "" "" "" "\n", cellbg,i, cellbg,proto_name(i), cellbg,proto_in[i], cellbg,proto_out[i], cellbg,proto_other[i], cellbg,t64); #else c_appendf(cnt, "" "" "" "" "" "\n", cellbg,i, cellbg,proto_name(i), cellbg,data_in = strint64(proto_in[i]), cellbg,data_out = strint64(proto_out[i]), cellbg,data_other = strint64(proto_other[i]), cellbg,total = strint64(t64)); free(data_in); free(data_out); free(data_other); free(total); #endif /* HAVE_64PRINT_COMMAS */ } c_appends(cnt, "
" "%s" "%s" "%s" "%s" "%s
%d%s%'Lu%'Lu%'Lu%'Lu
%d%s%s%s%s%s
" "
"); refresh_footer(cnt); return cnt; } static inline content *new_gif(void) { content *cnt = c_new(); assert(GIF_SIZE <= cnt->pool); memcpy(cnt->buf, gif, GIF_SIZE); cnt->used = GIF_SIZE; return cnt; } static content *info_menu(void) { content *c = c_new(); c_appendf(c, "" "%s" "" "

%s

", _("Info"), _("Info") ); c_appendf(c, "%s", _("Data off the wire") ); c_appendf(c, "" ); return c; } static content *info_bytes_total(void) { #ifndef HAVE_64PRINT_COMMAS char *x_data; #endif content *cnt = c_new(); #ifdef HAVE_64PRINT_COMMAS c_appendf(cnt, "%'Lu", total_data); #else c_appendf(cnt, "%s", x_data = strint64(total_data)); free(x_data); #endif return cnt; } static content *www_unimplemented(void) { content *c = c_new(); c_appendf(c, "" "

%s

%s" "", _("Not Implemented"), _("Whatever the heck you just requested, I can't generate.") ); return c; } static content *www_notfound(void) { content *c = c_new(); c_appendf(c, "" "

%s

%s" "", _("Not Found"), _("Whatever you just requested, I don't have.") ); return c; } static content *redirect(const char *url) { content *c = c_new(); c_appendf(c, "" "" "" "Moved here.", url, url); return c; } static SOCKET sb_socket[SCOREBOARD]; static int sb_state[SCOREBOARD]; #define STATE_OPEN 0 #define STATE_REQUEST 1 #define STATE_REQCOMPLETE 2 #define STATE_REPLY 3 #define MAX_REQ_SIZE 8192 static char sb_request[SCOREBOARD][MAX_REQ_SIZE]; static content *sb_reply[SCOREBOARD]; static ssize_t sb_sent[SCOREBOARD]; static void kill_connection(const int slot) { if (sb_socket[slot] != -1) { shutdown(sb_socket[slot], SHUT_WR); close(sb_socket[slot]); } sb_socket[slot] = -1; sb_state[slot] = STATE_OPEN; sb_request[slot][0] = 0; if (sb_reply[slot]) c_delete(&(sb_reply[slot])); assert(sb_reply[slot] == NULL); sb_sent[slot] = 0; } static int get_url(const char *request, const char *url) { char d = request[strlen(url)]; if (memcmp(request, url, strlen(url)) != 0) return 0; if (d == '\r' || d == '\n' || d == ' ' || d == '?') return 1; else return 0; } static void http_200_ok(content *hdr, const int is_cachable) { char datebuf[200]; time_t now = time(NULL); struct tm *tm = gmtime(&now); strftime(datebuf, 200, "%a, %e %b %Y %H:%M:%S GMT", tm); c_appendf(hdr, "HTTP/1.1 200 OK\r\n" "Date: %s\r\n" "Last-Modified: %s\r\n", datebuf, datebuf); if (!is_cachable) c_appendf(hdr, "Expires: %s\r\n", datebuf); else { now += 86400; tm = gmtime(&now); strftime(datebuf, 200, "%a, %e %b %Y %H:%M:%S GMT", tm); c_appendf(hdr, "Expires: %s\r\n", datebuf); } } static void handle_connection(const int slot) { content *hdr, *cnt; char *request = sb_request[slot], *tmp, *tmp2; int pos, recvd, is_gif = 0, is_txt = 0; SOCKET incoming = sb_socket[slot]; assert(incoming != -1); /* FIXME: check for connections that time out */ /* first, check if it's requesting */ if (sb_state[slot] == STATE_REQUEST) { pos = strlen(request); recvd = recv(incoming, request+pos, MAX_REQ_SIZE-1-pos, MSG_NOSIGNAL); request[pos+recvd] = 0; if (recvd == MAX_REQ_SIZE-1-pos) { if (verbose) printf("WWW(%d): Request %d exceeded " "size of %d.\n", getpid(), slot, MAX_REQ_SIZE); kill_connection(slot); return; } if (recvd < 1) { if (verbose) printf("WWW(%d): Request %d broke off.\n", getpid(), slot); kill_connection(slot); return; } if ( (strcmp(request+strlen(request)-2, "\n\n") == 0) || (strcmp(request+strlen(request)-2, "\r\r") == 0) || (strcmp(request+strlen(request)-4, "\r\n\r\n") == 0) || (strcmp(request+strlen(request)-4, "\n\r\n\r") == 0) ) { sb_state[slot] = STATE_REQCOMPLETE; shutdown(incoming, 0); /* no more recv */ #define CMP(x) (memcmp(request,x,strlen(x)) == 0) #define GET(x) (get_url(request, "GET " x)) hdr = c_new(); /* parse ?refresh= in URL */ refresh = 0; tmp = strstr(request, "Referer: "); if (!tmp) tmp = request + MAX_REQ_SIZE; /* cheater */ tmp2 = strstr(request, "?refresh="); if (tmp2 && tmp2 < tmp) refresh = atoi(tmp2+9); tmp2 = strstr(request, "cutitout="); if (tmp2 && tmp2 < tmp) refresh = 0; /* parse URL */ if (!CMP("GET ")) { c_appends(hdr, "HTTP/1.1 501 Not Implemented\r\n"); cnt = www_unimplemented(); } else if GET("/") cnt = www_frontpage(); else if GET("/hosts-total.html") cnt = www_hosts(TOTAL,1); else if GET("/hosts-total-full.html") cnt = www_hosts(TOTAL,0); else if GET("/hosts-ip.html") cnt = www_hosts(MAIN,1); else if GET("/hosts-ip-full.html") cnt = www_hosts(MAIN,0); else if GET("/hosts-in.html") cnt = www_hosts(IN,1); else if GET("/hosts-in-full.html") cnt = www_hosts(IN,0); else if GET("/hosts-out.html") cnt = www_hosts(OUT,1); else if GET("/hosts-out-full.html") cnt = www_hosts(OUT,0); else if GET("/ports-total.html") cnt = www_ports(TOTAL,1); else if GET("/ports-total-full.html") cnt = www_ports(TOTAL,0); else if GET("/ports-num.html") cnt = www_ports(MAIN,1); else if GET("/ports-num-full.html") cnt = www_ports(MAIN,0); else if GET("/ports-in.html") cnt = www_ports(IN,1); else if GET("/ports-in-full.html") cnt = www_ports(IN,0); else if GET("/ports-out.html") cnt = www_ports(OUT,1); else if GET("/ports-out-full.html") cnt = www_ports(OUT,0); else if GET("/protocols.html") cnt = www_protocols(); else if GET("/graphs.html") cnt = www_graphs(); else if CMP("GET /i/") { /* "validator" */ if (strstr(request,"If-Modified-Since:")) { sb_reply[slot] = hdr; c_appends(sb_reply[slot], "HTTP/1.1 304 Not Modified\r\n"); sb_state[slot] = STATE_REPLY; return; } cnt = new_gif(); is_gif = 1; if GET("/i/i") { cnt->buf[GIF_RED] = _I_R; cnt->buf[GIF_GREEN] = _I_G; cnt->buf[GIF_BLUE] = _I_B; } else if GET("/i/o") { cnt->buf[GIF_RED] = _O_R; cnt->buf[GIF_GREEN] = _O_G; cnt->buf[GIF_BLUE] = _O_B; } else cnt->buf[GIF_PIXEL] = GIF_TRANSPARENT; } else if CMP("GET /dns-") { c_appends(hdr, "HTTP/1.1 302 Found\r\n" "Location: /\r\n"); if GET("/dns-on.html") want_resolve = ON; else if GET("/dns-off.html") want_resolve = OFF; else if GET("/dns-cycle.html") want_resolve = IN_PROGRESS; cnt = redirect("/"); } else if CMP("GET /info") { if GET("/info") { c_appends(hdr, "HTTP/1.1 302 Found\r\n" "Location: /info/\r\n"); cnt = redirect("/info/"); } else if GET("/info/") cnt = info_menu(); else if GET("/info/bytes-total.txt") { is_txt = 1; cnt = info_bytes_total(); } } else { c_appends(hdr, "HTTP/1.1 404 Not Found\r\n"); cnt = www_notfound(); } #undef GET #undef CMP if (!hdr->used) http_200_ok(hdr, is_gif); if (is_gif) { c_appends(hdr, "Cache-Control: public\r\n" "Content-Type: image/gif\r\n"); } else { c_appends(hdr, "Cache-Control: no-cache, must-revalidate, " "max-age=0\r\n"); if (is_txt) c_appends(hdr, "Content-Type: text/plain\r\n"); else c_appends(hdr, "Content-Type: text/html; " "charset=UTF-8\r\n"); } c_appendf(hdr, "Content-Length: %d\r\n" "Connection: close\r\n" "\r\n", cnt->used); /* build final content buffer */ sb_reply[slot] = hdr; sb_sent[slot] = 0; c_appendsl(sb_reply[slot], cnt->buf, cnt->used); c_delete(&cnt); /* we can now start sending it */ sb_state[slot] = STATE_REPLY; } /* end of requesting code */ return; } /* it's not requesting, it could be replying */ if (sb_state[slot] == STATE_REPLY) { ssize_t sent = send(incoming, sb_reply[slot]->buf + sb_sent[slot], sb_reply[slot]->used - sb_sent[slot], MSG_NOSIGNAL); if (sent < 1) { if (verbose) printf("WWW(%d): Slot %d died during " "reply sending.\n", getpid(), slot); kill_connection(slot); return; } /* is it finished? */ if ((unsigned)sent == sb_reply[slot]->used - sb_sent[slot]) kill_connection(slot); else { if (verbose) printf("sent %d+%d, used %d\n", sb_sent[slot], sent, sb_reply[slot]->used); sb_sent[slot] += sent; } } } static RETSIGTYPE broken_pipe(int signum unused) { if (verbose) printf("WWW: Suppressed SIGPIPE.\n"); } SOCKET sockin; void www_poll(void) { int i, max_fd, sb_open = -1; struct timeval timeout; fd_set r_fd, w_fd; max_fd = sockin; FD_ZERO(&r_fd); FD_ZERO(&w_fd); for (i=0; i language version.\n" */ printf(_("WWW: You are using the English language version.\n")); #else printf("WWW: Compiled without NLS\n"); #endif init_linkbox(); if (!MSG_NOSIGNAL) /* sometimes send() and recv() throw it */ signal(SIGPIPE, broken_pipe); while (!shutting_down) www_poll(); close(sockin); for (i=0; i