// // Get an HTTP request header and write the response to the client. // gain here we assume that the socket buffer is enough without doing // any kind of userspace buffering. // // Returns 1 on error to signal the caller the client connection should // be closed. // int handleHTTPRequest(struct client *c, char *p) { char hdr[512]; int clen, hdrlen; int httpver, keepalive; char *url, *content; char ctype[48]; char getFile[1024]; char *ext; if (Modes.debug & MODES_DEBUG_NET) printf("\nHTTP request: %s\n", c->buf); // Minimally parse the request. httpver = (strstr(p, "HTTP/1.1") != NULL) ? 11 : 10; if (httpver == 10) { // HTTP 1.0 defaults to close, unless otherwise specified. keepalive = strstr(p, "Connection: keep-alive") != NULL; } else if (httpver == 11) { // HTTP 1.1 defaults to keep-alive, unless close is specified. keepalive = strstr(p, "Connection: close") == NULL; } // Identify he URL. p = strchr(p,' '); if (!p) return 1; // There should be the method and a space url = ++p; // Now this should point to the requested URL p = strchr(p, ' '); if (!p) return 1; // There should be a space before HTTP/ *p = '\0'; if (Modes.debug & MODES_DEBUG_NET) { printf("\nHTTP keep alive: %d\n", keepalive); printf("HTTP requested URL: %s\n\n", url); } if (strlen(url) < 2) { snprintf(getFile, sizeof getFile, "%s/gmap.html", HTMLPATH); // Default file } else { snprintf(getFile, sizeof getFile, "%s/%s", HTMLPATH, url); } // Select the content to send, we have just two so far: // "/" -> Our google map application. // "/data.json" -> Our ajax request to update planes. if (strstr(url, "/data.json")) { content = aircraftsToJson(&clen); //snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); } else { struct stat sbuf; int fd = -1; if (stat(getFile, &sbuf) != -1 && (fd = open(getFile, O_RDONLY)) != -1) { content = (char *) malloc(sbuf.st_size); if (read(fd, content, sbuf.st_size) == -1) { snprintf(content, sbuf.st_size, "Error reading from file: %s", strerror(errno)); } clen = sbuf.st_size; } else { char buf[128]; clen = snprintf(buf,sizeof(buf),"Error opening HTML file: %s", strerror(errno)); content = strdup(buf); } if (fd != -1) { close(fd); } } // Get file extension and content type snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_HTML); // Default content type ext = strrchr(getFile, '.'); if (strlen(ext) > 0) { if (strstr(ext, ".json")) { snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); } else if (strstr(ext, ".css")) { snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_CSS); } else if (strstr(ext, ".js")) { snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JS); } } // Create the header and send the reply hdrlen = snprintf(hdr, sizeof(hdr), "HTTP/1.1 200 OK\r\n" "Server: Dump1090\r\n" "Content-Type: %s\r\n" "Connection: %s\r\n" "Content-Length: %d\r\n" "Cache-Control: no-cache, must-revalidate\r\n" "Expires: Sat, 26 Jul 1997 05:00:00 GMT\r\n" "\r\n", ctype, keepalive ? "keep-alive" : "close", clen); if (Modes.debug & MODES_DEBUG_NET) { printf("HTTP Reply header:\n%s", hdr); } // Send header and content. if (write(c->fd, hdr, hdrlen) == -1 || write(c->fd, content, clen) == -1) { free(content); return 1; } free(content); Modes.stat_http_requests++; return !keepalive; }
// // Get an HTTP request header and write the response to the client. // gain here we assume that the socket buffer is enough without doing // any kind of userspace buffering. // // Returns 1 on error to signal the caller the client connection should // be closed. // int handleHTTPRequest(struct client *c, char *p) { char hdr[512]; int clen, hdrlen; int httpver, keepalive; int statuscode = 500; char *url, *content; char ctype[48]; char getFile[1024]; char *ext; if (Modes.debug & MODES_DEBUG_NET) printf("\nHTTP request: %s\n", c->buf); // Minimally parse the request. httpver = (strstr(p, "HTTP/1.1") != NULL) ? 11 : 10; if (httpver == 10) { // HTTP 1.0 defaults to close, unless otherwise specified. //keepalive = strstr(p, "Connection: keep-alive") != NULL; } else if (httpver == 11) { // HTTP 1.1 defaults to keep-alive, unless close is specified. //keepalive = strstr(p, "Connection: close") == NULL; } keepalive = 0; // Identify he URL. p = strchr(p,' '); if (!p) return 1; // There should be the method and a space url = ++p; // Now this should point to the requested URL p = strchr(p, ' '); if (!p) return 1; // There should be a space before HTTP/ *p = '\0'; if (Modes.debug & MODES_DEBUG_NET) { printf("\nHTTP keep alive: %d\n", keepalive); printf("HTTP requested URL: %s\n\n", url); } if (strlen(url) < 2) { snprintf(getFile, sizeof getFile, "%s/gmap.html", HTMLPATH); // Default file } else { snprintf(getFile, sizeof getFile, "%s/%s", HTMLPATH, url); } // Select the content to send, we have just two so far: // "/" -> Our google map application. // "/data.json" -> Our ajax request to update planes. if (strstr(url, "/data.json")) { statuscode = 200; content = aircraftsToJson(&clen); //snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); } else { struct stat sbuf; int fd = -1; char *rp, *hrp; rp = realpath(getFile, NULL); hrp = realpath(HTMLPATH, NULL); hrp = (hrp ? hrp : HTMLPATH); clen = -1; content = strdup("Server error occured"); if (rp && (!strncmp(hrp, rp, strlen(hrp)))) { if (stat(getFile, &sbuf) != -1 && (fd = open(getFile, O_RDONLY)) != -1) { content = (char *) realloc(content, sbuf.st_size); if (read(fd, content, sbuf.st_size) != -1) { clen = sbuf.st_size; statuscode = 200; } } } else { errno = ENOENT; } if (clen < 0) { content = realloc(content, 128); clen = snprintf(content, 128,"Error opening HTML file: %s", strerror(errno)); statuscode = 404; } if (fd != -1) { close(fd); } } // Get file extension and content type snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_HTML); // Default content type ext = strrchr(getFile, '.'); if (ext != NULL && strlen(ext) > 0) { if (strstr(ext, ".json")) { snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); } else if (strstr(ext, ".css")) { snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_CSS); } else if (strstr(ext, ".js")) { snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JS); } } // Create the header and send the reply hdrlen = snprintf(hdr, sizeof(hdr), "HTTP/1.1 %d \r\n" "Server: Dump1090\r\n" "Content-Type: %s\r\n" "Connection: %s\r\n" "Content-Length: %d\r\n" "Cache-Control: no-cache, must-revalidate\r\n" "Expires: Sat, 26 Jul 1997 05:00:00 GMT\r\n" "\r\n", statuscode, ctype, keepalive ? "keep-alive" : "close", clen); if (Modes.debug & MODES_DEBUG_NET) { printf("HTTP Reply header:\n%s", hdr); } // Send header and content. #ifndef _WIN32 if ( (write(c->fd, hdr, hdrlen) != hdrlen) || (write(c->fd, content, clen) != clen) ) { #else if ( (send(c->fd, hdr, hdrlen, 0) != hdrlen) || (send(c->fd, content, clen, 0) != clen) ) { #endif free(content); return 1; } free(content); Modes.stat_http_requests++; return !keepalive; } // //========================================================================= // // This function polls the clients using read() in order to receive new // messages from the net. // // The message is supposed to be separated from the next message by the // separator 'sep', which is a null-terminated C string. // // Every full message received is decoded and passed to the higher layers // calling the function's 'handler'. // // The handler returns 0 on success, or 1 to signal this function we should // close the connection with the client in case of non-recoverable errors. // void modesReadFromClient(struct client *c, char *sep, int(*handler)(struct client *, char *)) { int left; int nread; int fullmsg; int bContinue = 1; char *s, *e, *p; while(bContinue) { fullmsg = 0; left = MODES_CLIENT_BUF_SIZE - c->buflen; // If our buffer is full discard it, this is some badly formatted shit if (left <= 0) { c->buflen = 0; left = MODES_CLIENT_BUF_SIZE; // If there is garbage, read more to discard it ASAP } #ifndef _WIN32 nread = read(c->fd, c->buf+c->buflen, left); #else nread = recv(c->fd, c->buf+c->buflen, left, 0); if (nread < 0) {errno = WSAGetLastError();} #endif if (nread == 0) { modesCloseClient(c); return; } // If we didn't get all the data we asked for, then return once we've processed what we did get. if (nread != left) { bContinue = 0; } #ifndef _WIN32 if ( (nread < 0 && errno != EAGAIN && errno != EWOULDBLOCK) || nread == 0 ) { // Error, or end of file #else if ( (nread < 0) && (errno != EWOULDBLOCK)) { // Error, or end of file #endif modesCloseClient(c); return; } if (nread <= 0) { break; // Serve next client } c->buflen += nread; // Always null-term so we are free to use strstr() (it won't affect binary case) c->buf[c->buflen] = '\0'; e = s = c->buf; // Start with the start of buffer, first message if (c->service == Modes.bis) { // This is the Beast Binary scanning case. // If there is a complete message still in the buffer, there must be the separator 'sep' // in the buffer, note that we full-scan the buffer at every read for simplicity. left = c->buflen; // Length of valid search for memchr() while (left && ((s = memchr(e, (char) 0x1a, left)) != NULL)) { // The first byte of buffer 'should' be 0x1a s++; // skip the 0x1a if (*s == '1') { e = s + MODEAC_MSG_BYTES + 8; // point past remainder of message } else if (*s == '2') { e = s + MODES_SHORT_MSG_BYTES + 8; } else if (*s == '3') { e = s + MODES_LONG_MSG_BYTES + 8; } else { e = s; // Not a valid beast message, skip left = &(c->buf[c->buflen]) - e; continue; } // we need to be careful of double escape characters in the message body for (p = s; p < e; p++) { if (0x1A == *p) { p++; e++; if (e > &(c->buf[c->buflen])) { break; } } } left = &(c->buf[c->buflen]) - e; if (left < 0) { // Incomplete message in buffer e = s - 1; // point back at last found 0x1a. break; } // Have a 0x1a followed by 1, 2 or 3 - pass message less 0x1a to handler. if (handler(c, s)) { modesCloseClient(c); return; } fullmsg = 1; } s = e; // For the buffer remainder below } else { // // This is the ASCII scanning case, AVR RAW or HTTP at present // If there is a complete message still in the buffer, there must be the separator 'sep' // in the buffer, note that we full-scan the buffer at every read for simplicity. // while ((e = strstr(s, sep)) != NULL) { // end of first message if found *e = '\0'; // The handler expects null terminated strings if (handler(c, s)) { // Pass message to handler. modesCloseClient(c); // Handler returns 1 on error to signal we . return; // should close the client connection } s = e + strlen(sep); // Move to start of next message fullmsg = 1; } } if (fullmsg) { // We processed something - so c->buflen = &(c->buf[c->buflen]) - s; // Update the unprocessed buffer length memmove(c->buf, s, c->buflen); // Move what's remaining to the start of the buffer } else { // If no message was decoded process the next client break; } } } // //========================================================================= // // Read data from clients. This function actually delegates a lower-level // function that depends on the kind of service (raw, http, ...). // void modesReadFromClients(void) { struct client *c = modesAcceptClients(); while (c) { // Read next before servicing client incase the service routine deletes the client! struct client *next = c->next; if (c->fd >= 0) { if (c->service == Modes.ris) { modesReadFromClient(c,"\n",decodeHexMessage); } else if (c->service == Modes.bis) { modesReadFromClient(c,"",decodeBinMessage); } else if (c->service == Modes.https) { modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest); } } else { modesFreeClient(c); } c = next; } }