/* helper function for reading data from a client */ int client_read_bytes (client_t *client, void *buf, unsigned len) { int (*con_read)(struct connection_tag *handle, void *buf, size_t len) = connection_read; int bytes; if (len == 0) return 0; if (client->refbuf && client->pos < client->refbuf->len) { unsigned remaining = client->refbuf->len - client->pos; if (remaining > len) remaining = len; memcpy (buf, client->refbuf->data + client->pos, remaining); client->pos += remaining; if (client->pos >= client->refbuf->len) client_set_queue (client, NULL); return remaining; } #ifdef HAVE_OPENSSL if (client->connection.ssl) con_read = connection_read_ssl; #endif bytes = con_read (&client->connection, buf, len); if (bytes == -1 && client->connection.error) DEBUG0 ("reading from connection has failed"); return bytes; }
int main(int argc, char* argv[]) { int opt; bool vns_console = false; char* command_file = NULL; char* pid_file = NULL; int i, ifcount = 0; struct vnlif iflist[vnlif_maxcount]; int command_fd, command_eth = -1; FILE* pid_fd; struct pollfd pollfds[2+2*vnlif_maxcount]; int pollres; char buffer[MSGSIZE]; int len; char* pktbuffer = buffer + MSGHDRSIZE; c_base* vnsbasehdr = (c_base*)buffer; c_hwinfo* vnshwinfo = (c_hwinfo*)buffer; c_packet_header* vnspkthdr = (c_packet_header*)buffer; // ---------------- parse command options ---------------- while ((opt = getopt(argc, argv, "si:c:p:")) != -1) { switch (opt) { case 's': vns_console = true; break; case 'i': if (ifcount < vnlif_maxcount) { if (!vnlif_parse(iflist + ifcount, optarg)) die("vnlif_parse"); } else { die("cmdline vnlif_maxcount"); } ++ifcount; break; case 'c': command_file = optarg; break; case 'p': pid_file = optarg; break; } } if (ifcount == 0 || command_file == NULL || pid_file == NULL) { printf("vnlsvc: Virtual Network Lab Service\nTAP-UDP: vnlsvc -i eth0/tap0/1a:8e:22:b1:da:f1/198.51.100.1#255.255.255.0/192.0.2.1:2001/192.0.2.2:2001/ -c /tmp/command.pipe -p /tmp/vnltun.pid\nVNSconsole-UDP: vnlsvc -s -i eth0//1a:8e:22:b1:da:f1/198.51.100.1#255.255.255.0/192.0.2.1:2001/192.0.2.2:2001/ -c /tmp/command.pipe -p /tmp/vnltun.pid\n"); exit(1); } // ---------------- open files and sockets ---------------- command_fd = open(command_file, O_RDONLY | O_NONBLOCK); if (command_fd < 0) die("command open"); pollfds[0].fd = 0; pollfds[0].events = vns_console ? POLLIN : 0; pollfds[1].fd = command_fd; pollfds[1].events = POLLIN; for (i = 0; i < ifcount; ++i) { pollfds[2+i].fd = iflist[i].udp_fd = udp_open(&(iflist[i].tip), &(iflist[i].rtip)); pollfds[2+i].events = POLLIN; if (!vns_console) { pollfds[2+ifcount+i].fd = iflist[i].tap_fd = tap_open(iflist[i].tapname); pollfds[2+ifcount+i].events = POLLIN; } } //pollfds: 0=stdin, 1=command pipe, 2..2+ifcount-1=UDP, 2+ifcount..2+2*ifcount-1=TAP // ---------------- initialize ---------------- pid_fd = fopen(pid_file, "w"); if (pid_fd == NULL) die("pid output"); fprintf(pid_fd, "%d", getpid()); fclose(pid_fd); srand(time(NULL)); if (vns_console) { con_init(); vns_wAuthReq(buffer); con_write(buffer); } // ---------------- main poll loop ---------------- while (1) { pollres = poll(pollfds, 2+(vns_console?1:2)*ifcount, -1); if (pollres == -1) die("poll"); else if (pollres == 0) { perror("poll timeout"); continue; } /* for (i = 0; i < 2+(vns_console?1:2)*ifcount; ++i) { if (pollfds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { FILE* dbglog = fopen("/tmp/vnlsvc.debug.log","a"); fprintf(dbglog,"%d %d %d %x\n",time(NULL),getpid(),i,pollfds[i].revents); fclose(dbglog); } } */ // ---------------- VNS protocol, console to UDP ---------------- if (vns_console && (pollfds[0].revents & POLLIN)) { if (con_read(buffer)) { switch (vns_getType(buffer)) { case VNS_AUTH_REPLY: vns_wAuthStatus(buffer); con_write(buffer); break; case VNSOPEN: iflist_hwinfo(vnshwinfo, iflist, ifcount); con_write(buffer); break; case VNSPACKET: i = iflist_find(iflist, ifcount, vnspkthdr->mInterfaceName); if (i < 0) break; len = ntohl(vnspkthdr->mLen) - sizeof(c_packet_header); if (lossy_filter(iflist[i].lossy)) { udp_write(iflist[i].udp_fd, pktbuffer, len, &(iflist[i].rtip)); } break; } } } // ---------------- console error ---------------- if (vns_console && (pollfds[0].revents & (POLLERR | POLLHUP))) { die("console error"); } // ---------------- setlossy command ---------------- if (pollfds[1].revents & POLLIN) { if (command_eth < 0 && 1 == read(command_fd, buffer, 1)) { command_eth = buffer[0]; } if (1 == read(command_fd, buffer, 1) && command_eth < ifcount) { iflist[command_eth].lossy = buffer[0]; } } if (pollfds[1].revents & POLLHUP) { command_eth = -1; close(command_fd); pollfds[1].fd = command_fd = open(command_file, O_RDONLY | O_NONBLOCK); if (command_fd < 0) die("command reopen"); } for (i = 0; i < ifcount; ++i) { // ---------------- UDP to console/TAP ---------------- if (pollfds[2+i].revents & POLLIN) { len = udp_read(iflist[i].udp_fd, pktbuffer, &(iflist[i].rtip)); if (len > 0) { if (vns_console) { vns_wPacketHdr(buffer, len, iflist[i].ifname); con_write(buffer); } else { tap_write(iflist[i].tap_fd, pktbuffer, len); } } } // ---------------- UDP error ---------------- if (pollfds[2+i].revents & POLLERR) { int sockerror; socklen_t socklen = sizeof(int); getsockopt(iflist[i].udp_fd, SOL_SOCKET, SO_ERROR, &sockerror, &socklen); } // ---------------- TAP to UDP ---------------- if (!vns_console && (pollfds[2+ifcount+i].revents & POLLIN)) { len = tap_read(iflist[i].tap_fd, pktbuffer); if (len > 0 && lossy_filter(iflist[i].lossy)) { udp_write(iflist[i].udp_fd, pktbuffer, len, &(iflist[i].rtip)); } } } } }
/** * @brief Extract the contents of a literal string and advance the position of the parser stream. * @note This function expects as input a string beginning with '{' and followed by a numerical string, an optional '+', and a closing '}'. After reading in the numerical size parameter, it then attempts to read in that many bytes of input from the network stream. * @param con the client IMAP connection passing the literal string as input to the server. * @param output the address of a managed string that will receive a copy of the literal string's contents on success, or NULL on failure or if it is zero length. * @param start the address of a pointer to the start of the buffer to be parsed (beginning with '{'), that will also be updated to * point to the next argument in the sequence on success. * @param length a pointer to a size_t variable that contains the length of the string to be parsed, and that will be updated to reflect * the length of the remainder of the input string that follows the parsed literal string. * @return -1 on general or parse error or if an enclosing pair of double quotes was not found, or 1 if the supplied quoted string was valid. */ int_t imap_parse_literal(connection_t *con, stringer_t **output, chr_t **start, size_t *length) { chr_t *holder; int_t plus = 0; stringer_t *result; size_t characters, left; ssize_t nread; uint64_t literal, number; // Get setup. holder = *start; left = *length; *output = NULL; // Skip the opening bracket. if (*holder != '{' || !left) { return -1; } else { holder++; left--; } // Advance until we have a break character. while (left && *holder >= '0' && *holder <= '9') { holder++; left--; } // Store the length. characters = holder - *start - 1; if (left && *holder == '+') { plus = 1; holder++; left--; } if (*holder != '}' || !characters) { return -1; } // Convert to a number. Make sure the number is positive. if (!uint64_conv_bl(*start + 1, characters, &number)) { return -1; } literal = (size_t)number; // If the number is larger than 128 megabytes, then reject it. if (!plus && number > 134217728) { return -1; } // They client is already transmitting, so read the entire file, then reject it. else if (number > 134217728) { while (number > 0) { // Read the data. if ((nread = con_read(con)) <= 0) { log_pedantic("The connection was dropped while reading the literal."); return -1; } // Deal with signedness problem. characters = nread; if (number > (uint64_t)characters) { number -= characters; } else { // If we have any extra characters in the buffer, move them to the beginning. if ((uint64_t)characters > number) { mm_move(st_char_get(con->network.buffer), st_char_get(con->network.buffer) + number, characters - number); st_length_set(con->network.buffer, characters - number); con->network.line = line_pl_st(con->network.buffer, 0); } else { st_length_set(con->network.buffer, 0); con->network.line = pl_null(); } // Make sure we have a full line. if (pl_empty(con->network.line) && con_read_line(con, true) <= 0) { log_pedantic("The connection was dropped while reading the literal."); return -1; } number = 0; } } return -1; } // If this is not a plus literal, output the proceed statement. if (!plus) { con_write_bl(con, "+ GO\r\n", 6); } // Handle the special case of a zero length literal. if (literal == 0) { // Read the next line. if (con_read_line(con, true) <= 0) { log_pedantic("The connection was dropped while reading the literal."); return -1; } *start = st_char_get(con->network.buffer); *length = pl_length_get(con->network.line); // There should be a space before the next argument. if (*length && **start == ' ') { (*start)++; (*length)--; } return 1; } // Allocate a stringer for the buffer. if (!(result = st_alloc(literal))) { log_pedantic("Unable to allocate a buffer of %lu bytes for the literal argument.", literal); return -1; } // So we know how many more characters to read. left = literal; // Where we put the data. holder = st_char_get(result); // Keep looping until we run out of data. while (left) { // Read the data. if ((nread = con_read(con)) <= 0) { log_pedantic("The connection was dropped while reading the literal."); st_free(result); return -1; } characters = nread; // If we have a buffer, copy the data into the buffer. mm_copy(holder, st_char_get(con->network.buffer), (left > characters) ? characters : left); if (left > characters) { holder += characters; left -= characters; } else { st_length_set(result, literal); // If we have any extra characters in the buffer, move them to the beginning. if (characters > left) { mm_move(st_char_get(con->network.buffer), st_char_get(con->network.buffer) + left, characters - left); st_length_set(con->network.buffer, characters - left); con->network.line = line_pl_st(con->network.buffer, 0); } else { st_length_set(con->network.buffer, 0); con->network.line = pl_null(); } // Make sure we have a full line. if (pl_empty(con->network.line) && con_read_line(con, true) <= 0) { log_pedantic("The connection was dropped while reading the literal."); st_free(result); return -1; } left = 0; } } *start = st_char_get(con->network.buffer); *length = pl_length_get(con->network.line); // There should be a space before the next argument. if (*length && **start == ' ') { (*start)++; (*length)--; } if (result != NULL) { *output = result; } else { return -1; } return 1; }