// dump1090, a Mode S messages decoder for RTLSDR devices. // // Copyright (C) 2012 by Salvatore Sanfilippo // // All rights reserved. // // 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. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "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 THE COPYRIGHT // HOLDER OR CONTRIBUTORS 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 "dump1090.h" // // ============================= Networking ============================= // // Note: here we disregard any kind of good coding practice in favor of // extreme simplicity, that is: // // 1) We only rely on the kernel buffers for our I/O without any kind of // user space buffering. // 2) We don't register any kind of event handler, from time to time a // function gets called and we accept new connections. All the rest is // handled via non-blocking I/O and manually polling clients to see if // they have something new to share with us when reading is needed. // //========================================================================= // // Networking "stack" initialization // void modesInitNet(void) { struct { char *descr; int *socket; int port; } services[6] = { {"Raw TCP output", &Modes.ros, Modes.net_output_raw_port}, {"Raw TCP input", &Modes.ris, Modes.net_input_raw_port}, {"Beast TCP output", &Modes.bos, Modes.net_output_beast_port}, {"Beast TCP input", &Modes.bis, Modes.net_input_beast_port}, {"HTTP server", &Modes.https, Modes.net_http_port}, {"Basestation TCP output", &Modes.sbsos, Modes.net_output_sbs_port} }; int j; memset(Modes.clients,0,sizeof(Modes.clients)); Modes.maxfd = -1; for (j = 0; j < 6; j++) { int s = anetTcpServer(Modes.aneterr, services[j].port, NULL); if (s == -1) { fprintf(stderr, "Error opening the listening port %d (%s): %s\n", services[j].port, services[j].descr, strerror(errno)); exit(1); } anetNonBlock(Modes.aneterr, s); *services[j].socket = s; } signal(SIGPIPE, SIG_IGN); } // //========================================================================= // // This function gets called from time to time when the decoding thread is // awakened by new data arriving. This usually happens a few times every second // void modesAcceptClients(void) { int fd, port; unsigned int j; struct client *c; int services[6]; services[0] = Modes.ros; services[1] = Modes.ris; services[2] = Modes.bos; services[3] = Modes.bis; services[4] = Modes.https; services[5] = Modes.sbsos; for (j = 0; j < sizeof(services)/sizeof(int); j++) { fd = anetTcpAccept(Modes.aneterr, services[j], NULL, &port); if (fd == -1) continue; if (fd >= MODES_NET_MAX_FD) { close(fd); return; // Max number of clients reached } anetNonBlock(Modes.aneterr, fd); c = (struct client *) malloc(sizeof(*c)); c->service = services[j]; c->fd = fd; c->buflen = 0; Modes.clients[fd] = c; anetSetSendBuffer(Modes.aneterr,fd,MODES_NET_SNDBUF_SIZE); if (Modes.maxfd < fd) Modes.maxfd = fd; if (services[j] == Modes.sbsos) Modes.stat_sbs_connections++; if (services[j] == Modes.ros) Modes.stat_raw_connections++; if (services[j] == Modes.bos) Modes.stat_beast_connections++; j--; // Try again with the same listening port if (Modes.debug & MODES_DEBUG_NET) printf("Created new client %d\n", fd); } } // //========================================================================= // // On error free the client, collect the structure, adjust maxfd if needed. // void modesFreeClient(int fd) { close(fd); if (Modes.clients[fd]->service == Modes.sbsos) { if (Modes.stat_sbs_connections) Modes.stat_sbs_connections--; } else if (Modes.clients[fd]->service == Modes.ros) { if (Modes.stat_raw_connections) Modes.stat_raw_connections--; } else if (Modes.clients[fd]->service == Modes.bos) { if (Modes.stat_beast_connections) Modes.stat_beast_connections--; } free(Modes.clients[fd]); Modes.clients[fd] = NULL; if (Modes.debug & MODES_DEBUG_NET) printf("Closing client %d\n", fd); // If this was our maxfd, rescan the full clients array to check what's // the new max. if (Modes.maxfd == fd) { int j; Modes.maxfd = -1; for (j = 0; j < MODES_NET_MAX_FD; j++) { if (Modes.clients[j]) Modes.maxfd = j; } } } // //========================================================================= // // Send the specified message to all clients listening for a given service // void modesSendAllClients(int service, void *msg, int len) { int j; struct client *c; for (j = 0; j <= Modes.maxfd; j++) { c = Modes.clients[j]; if (c && c->service == service) { int nwritten = write(j, msg, len); if (nwritten != len) { modesFreeClient(j); } } } } // //========================================================================= // // Write raw output in Beast Binary format with Timestamp to TCP clients // void modesSendBeastOutput(struct modesMessage *mm) { char *p = &Modes.beastOut[Modes.beastOutUsed]; int msgLen = mm->msgbits / 8; char * pTimeStamp; int j; *p++ = 0x1a; if (msgLen == MODES_SHORT_MSG_BYTES) {*p++ = '2';} else if (msgLen == MODES_LONG_MSG_BYTES) {*p++ = '3';} else if (msgLen == MODEAC_MSG_BYTES) {*p++ = '1';} else {return;} pTimeStamp = (char *) &mm->timestampMsg; for (j = 5; j >= 0; j--) { *p++ = pTimeStamp[j]; } *p++ = mm->signalLevel; memcpy(p, mm->msg, msgLen); Modes.beastOutUsed += (msgLen + 9); if (Modes.beastOutUsed >= Modes.net_output_raw_size) { modesSendAllClients(Modes.bos, Modes.beastOut, Modes.beastOutUsed); Modes.beastOutUsed = 0; Modes.net_output_raw_rate_count = 0; } } // //========================================================================= // // Write raw output to TCP clients // void modesSendRawOutput(struct modesMessage *mm) { char *p = &Modes.rawOut[Modes.rawOutUsed]; int msgLen = mm->msgbits / 8; int j; unsigned char * pTimeStamp; if (Modes.mlat && mm->timestampMsg) { *p++ = '@'; pTimeStamp = (unsigned char *) &mm->timestampMsg; for (j = 5; j >= 0; j--) { sprintf(p, "%02X", pTimeStamp[j]); p += 2; } Modes.rawOutUsed += 12; // additional 12 characters for timestamp } else *p++ = '*'; for (j = 0; j < msgLen; j++) { sprintf(p, "%02X", mm->msg[j]); p += 2; } *p++ = ';'; *p++ = '\n'; Modes.rawOutUsed += ((msgLen*2) + 3); if (Modes.rawOutUsed >= Modes.net_output_raw_size) { modesSendAllClients(Modes.ros, Modes.rawOut, Modes.rawOutUsed); Modes.rawOutUsed = 0; Modes.net_output_raw_rate_count = 0; } } // //========================================================================= // // Write SBS output to TCP clients // The message structure mm->bFlags tells us what has been updated by this message // void modesSendSBSOutput(struct modesMessage *mm) { char msg[256], *p = msg; uint32_t offset; struct timeb epocTime; struct tm stTime; int msgType; // // SBS BS style output checked against the following reference // http://www.homepages.mcb.net/bones/SBS/Article/Barebones42_Socket_Data.htm - seems comprehensive // // Decide on the basic SBS Message Type if ((mm->msgtype == 4) || (mm->msgtype == 20)) { msgType = 5; } else if ((mm->msgtype == 5) || (mm->msgtype == 21)) { msgType = 6; } else if ((mm->msgtype == 0) || (mm->msgtype == 16)) { msgType = 7; } else if (mm->msgtype == 11) { msgType = 8; } else if ((mm->msgtype != 17) && (mm->msgtype != 18)) { return; } else if ((mm->metype >= 1) && (mm->metype <= 4)) { msgType = 1; } else if ((mm->metype >= 5) && (mm->metype <= 8)) { if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {msgType = 2;} else {msgType = 7;} } else if ((mm->metype >= 9) && (mm->metype <= 18)) { if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {msgType = 3;} else {msgType = 7;} } else if (mm->metype != 19) { return; } else if ((mm->mesub == 1) || (mm->mesub == 2)) { msgType = 4; } else { return; } // Fields 1 to 6 : SBS message type and ICAO address of the aircraft and some other stuff p += sprintf(p, "MSG,%d,111,11111,%06X,111111,", msgType, mm->addr); // Fields 7 & 8 are the current time and date if (mm->timestampMsg) { // Make sure the records' timestamp is valid before outputing it epocTime = Modes.stSystemTimeBlk; // This is the time of the start of the Block we're processing offset = (int) (mm->timestampMsg - Modes.timestampBlk); // This is the time (in 12Mhz ticks) into the Block offset = offset / 12000; // convert to milliseconds epocTime.millitm += offset; // add on the offset time to the Block start time if (epocTime.millitm > 999) // if we've caused an overflow into the next second... {epocTime.millitm -= 1000; epocTime.time ++;} // ..correct the overflow stTime = *localtime(&epocTime.time); // convert the time to year, month day, hours, min, sec p += sprintf(p, "%04d/%02d/%02d,", (stTime.tm_year+1900),(stTime.tm_mon+1), stTime.tm_mday); p += sprintf(p, "%02d:%02d:%02d.%03d,", stTime.tm_hour, stTime.tm_min, stTime.tm_sec, epocTime.millitm); } else { p += sprintf(p, ",,"); } // Fields 9 & 10 are the current time and date ftime(&epocTime); // get the current system time & date stTime = *localtime(&epocTime.time); // convert the time to year, month day, hours, min, sec p += sprintf(p, "%04d/%02d/%02d,", (stTime.tm_year+1900),(stTime.tm_mon+1), stTime.tm_mday); p += sprintf(p, "%02d:%02d:%02d.%03d", stTime.tm_hour, stTime.tm_min, stTime.tm_sec, epocTime.millitm); // Field 11 is the callsign (if we have it) if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {p += sprintf(p, ",%s", mm->flight);} else {p += sprintf(p, ",");} // Field 12 is the altitude (if we have it) - force to zero if we're on the ground if ((mm->bFlags & MODES_ACFLAGS_AOG_GROUND) == MODES_ACFLAGS_AOG_GROUND) { p += sprintf(p, ",0"); } else if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { p += sprintf(p, ",%d", mm->altitude); } else { p += sprintf(p, ","); } // Field 13 and 14 are the ground Speed and Heading (if we have them) if (mm->bFlags & MODES_ACFLAGS_NSEWSPD_VALID) {p += sprintf(p, ",%d,%d", mm->velocity, mm->heading);} else {p += sprintf(p, ",,");} // Fields 15 and 16 are the Lat/Lon (if we have it) if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {p += sprintf(p, ",%1.5f,%1.5f", mm->fLat, mm->fLon);} else {p += sprintf(p, ",,");} // Field 17 is the VerticalRate (if we have it) if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {p += sprintf(p, ",%d", mm->vert_rate);} else {p += sprintf(p, ",");} // Field 18 is the Squawk (if we have it) if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {p += sprintf(p, ",%x", mm->modeA);} else {p += sprintf(p, ",");} // Field 19 is the Squawk Changing Alert flag (if we have it) if (mm->bFlags & MODES_ACFLAGS_FS_VALID) { if ((mm->fs >= 2) && (mm->fs <= 4)) { p += sprintf(p, ",-1"); } else { p += sprintf(p, ",0"); } } else { p += sprintf(p, ","); } // Field 20 is the Squawk Emergency flag (if we have it) if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { if ((mm->modeA == 0x7500) || (mm->modeA == 0x7600) || (mm->modeA == 0x7700)) { p += sprintf(p, ",-1"); } else { p += sprintf(p, ",0"); } } else { p += sprintf(p, ","); } // Field 21 is the Squawk Ident flag (if we have it) if (mm->bFlags & MODES_ACFLAGS_FS_VALID) { if ((mm->fs >= 4) && (mm->fs <= 5)) { p += sprintf(p, ",-1"); } else { p += sprintf(p, ",0"); } } else { p += sprintf(p, ","); } // Field 22 is the OnTheGround flag (if we have it) if (mm->bFlags & MODES_ACFLAGS_AOG_VALID) { if (mm->bFlags & MODES_ACFLAGS_AOG) { p += sprintf(p, ",-1"); } else { p += sprintf(p, ",0"); } } else { p += sprintf(p, ","); } p += sprintf(p, "\r\n"); modesSendAllClients(Modes.sbsos, msg, p-msg); } // //========================================================================= // void modesQueueOutput(struct modesMessage *mm) { if (Modes.stat_sbs_connections) {modesSendSBSOutput(mm);} if (Modes.stat_beast_connections) {modesSendBeastOutput(mm);} if (Modes.stat_raw_connections) {modesSendRawOutput(mm);} } // //========================================================================= // // This function decodes a Beast binary format message // // The message is passed to the higher level layers, so it feeds // the selected screen output, the network output and so forth. // // If the message looks invalid it is silently discarded. // // The function always returns 0 (success) to the caller as there is no // case where we want broken messages here to close the client connection. // int decodeBinMessage(struct client *c, char *p) { int msgLen = 0; unsigned char msg[MODES_LONG_MSG_BYTES]; struct modesMessage mm; MODES_NOTUSED(c); memset(&mm, 0, sizeof(mm)); if ((*p == '1') && (Modes.mode_ac)) { // skip ModeA/C unless user enables --modes-ac msgLen = MODEAC_MSG_BYTES; } else if (*p == '2') { msgLen = MODES_SHORT_MSG_BYTES; } else if (*p == '3') { msgLen = MODES_LONG_MSG_BYTES; } if (msgLen) { // Mark messages received over the internet as remote so that we don't try to // pass them off as being received by this instance when forwarding them mm.remote = 1; p += 7; // Skip the timestamp mm.signalLevel = *p++; // Grab the signal level memcpy(msg, p, msgLen); // and the data if (msgLen == MODEAC_MSG_BYTES) { // ModeA or ModeC decodeModeAMessage(&mm, ((msg[0] << 8) | msg[1])); } else { decodeModesMessage(&mm, msg); } useModesMessage(&mm); } return (0); } // //========================================================================= // // Turn an hex digit into its 4 bit decimal value. // Returns -1 if the digit is not in the 0-F range. // int hexDigitVal(int c) { c = tolower(c); if (c >= '0' && c <= '9') return c-'0'; else if (c >= 'a' && c <= 'f') return c-'a'+10; else return -1; } // //========================================================================= // // This function decodes a string representing message in raw hex format // like: *8D4B969699155600E87406F5B69F; The string is null-terminated. // // The message is passed to the higher level layers, so it feeds // the selected screen output, the network output and so forth. // // If the message looks invalid it is silently discarded. // // The function always returns 0 (success) to the caller as there is no // case where we want broken messages here to close the client connection. // int decodeHexMessage(struct client *c, char *hex) { int l = strlen(hex), j; unsigned char msg[MODES_LONG_MSG_BYTES]; struct modesMessage mm; MODES_NOTUSED(c); memset(&mm, 0, sizeof(mm)); // Mark messages received over the internet as remote so that we don't try to // pass them off as being received by this instance when forwarding them mm.remote = 1; mm.signalLevel = 0xFF; // Remove spaces on the left and on the right while(l && isspace(hex[l-1])) { hex[l-1] = '\0'; l--; } while(isspace(*hex)) { hex++; l--; } // Turn the message into binary. // Accept *-AVR raw @-AVR/BEAST timeS+raw %-AVR timeS+raw (CRC good) <-BEAST timeS+sigL+raw // and some AVR records that we can understand if (hex[l-1] != ';') {return (0);} // not complete - abort switch(hex[0]) { case '<': { mm.signalLevel = (hexDigitVal(hex[13])<<4) | hexDigitVal(hex[14]); hex += 15; l -= 16; // Skip <, timestamp and siglevel, and ; break;} case '@': // No CRC check case '%': { // CRC is OK hex += 13; l -= 14; // Skip @,%, and timestamp, and ; break;} case '*': case ':': { hex++; l-=2; // Skip * and ; break;} default: { return (0); // We don't know what this is, so abort break;} } if ( (l != (MODEAC_MSG_BYTES * 2)) && (l != (MODES_SHORT_MSG_BYTES * 2)) && (l != (MODES_LONG_MSG_BYTES * 2)) ) {return (0);} // Too short or long message... broken if ( (0 == Modes.mode_ac) && (l == (MODEAC_MSG_BYTES * 2)) ) {return (0);} // Right length for ModeA/C, but not enabled for (j = 0; j < l; j += 2) { int high = hexDigitVal(hex[j]); int low = hexDigitVal(hex[j+1]); if (high == -1 || low == -1) return 0; msg[j/2] = (high << 4) | low; } if (l == (MODEAC_MSG_BYTES * 2)) { // ModeA or ModeC decodeModeAMessage(&mm, ((msg[0] << 8) | msg[1])); } else { // Assume ModeS decodeModesMessage(&mm, msg); } useModesMessage(&mm); return (0); } // //========================================================================= // // Return a description of planes in json. No metric conversion // char *aircraftsToJson(int *len) { time_t now = time(NULL); struct aircraft *a = Modes.aircrafts; int buflen = 1024; // The initial buffer is incremented as needed char *buf = (char *) malloc(buflen), *p = buf; int l; l = snprintf(p,buflen,"[\n"); p += l; buflen -= l; while(a) { int position = 0; int track = 0; if (a->modeACflags & MODEAC_MSG_FLAG) { // skip any fudged ICAO records Mode A/C a = a->next; continue; } if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) { position = 1; } if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) { track = 1; } // No metric conversion l = snprintf(p,buflen, "{\"hex\":\"%06x\", \"squawk\":\"%04x\", \"flight\":\"%s\", \"lat\":%f, " "\"lon\":%f, \"validposition\":%d, \"altitude\":%d, \"vert_rate\":%d,\"track\":%d, \"validtrack\":%d," "\"speed\":%d, \"messages\":%ld, \"seen\":%d},\n", a->addr, a->modeA, a->flight, a->lat, a->lon, position, a->altitude, a->vert_rate, a->track, track, a->speed, a->messages, (int)(now - a->seen)); p += l; buflen -= l; //Resize if needed if (buflen < 256) { int used = p-buf; buflen += 1024; // Our increment. buf = (char *) realloc(buf,used+buflen); p = buf+used; } a = a->next; } //Remove the final comma if any, and closes the json array. if (*(p-2) == ',') { *(p-2) = '\n'; p--; buflen++; } l = snprintf(p,buflen,"]\n"); p += l; buflen -= l; *len = p-buf; return buf; } // //========================================================================= // #define MODES_CONTENT_TYPE_HTML "text/html;charset=utf-8" #define MODES_CONTENT_TYPE_CSS "text/css;charset=utf-8" #define MODES_CONTENT_TYPE_JSON "application/json;charset=utf-8" #define MODES_CONTENT_TYPE_JS "application/javascript;charset=utf-8" // // 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" "\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; } // //========================================================================= // // 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; 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 } nread = read(c->fd, c->buf+c->buflen, left); // 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; } if ( (nread < 0) && (errno != EAGAIN)) { // Error, or end of file modesFreeClient(c->fd); } 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; } 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)) { modesFreeClient(c->fd); 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. modesFreeClient(c->fd); // 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) { int j; struct client *c; modesAcceptClients(); for (j = 0; j <= Modes.maxfd; j++) { if ((c = Modes.clients[j]) == NULL) continue; 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); } } // // =============================== Network IO =========================== //