int core_connect(nc_sock_t *ncsock) { assert(ncsock); if (ncsock->proto == NETCAT_PROTO_TCP) return ncsock->fd = core_tcp_connect(ncsock); else if (ncsock->proto == NETCAT_PROTO_UDP) return ncsock->fd = core_udp_connect(ncsock); else abort(); return -1; }
/** * Send a file to a TFTP server * * @param:inode, the inode to store our state in * @param:ip, the ip to contact to get the file * @param:filename, the file we wanna push * * @out: open_file_t structure, stores in file->open_file * @out: the lenght of this file, stores in file->file_len * */ __export int tftp_put(struct url_info *url, int flags, struct inode *inode, const char **redir, char *data, int data_length) { struct pxe_pvt_inode *socket = PVT(inode); char *buf; uint16_t buf_len; static const char wrq_tail[] = "octet"; char wrq_packet_buf[512+4+6]; char reply_packet_buf[PKTBUF_SIZE]; int err; int wrq_len; const uint8_t *timeout_ptr; jiffies_t timeout; jiffies_t oldtime; uint16_t opcode; uint16_t src_port = url->port; uint32_t src_ip; uint16_t seq = 0; size_t chunk = 0; int len = data_length; int return_code = -ntohs(TFTP_EUNDEF); (void)redir; /* TFTP does not redirect */ (void)flags; if (url->type != URL_OLD_TFTP) { /* * The TFTP URL specification allows the TFTP to end with a * ;mode= which we just ignore. */ url_unescape(url->path, ';'); } if (!src_port) src_port = TFTP_PORT; // socket->ops = &tftp_conn_ops; if (core_udp_open(socket)) return return_code; buf = wrq_packet_buf; *(uint16_t *)buf = TFTP_WRQ; /* TFTP opcode */ buf += 2; buf += strlcpy(buf, url->path, 512); buf++; /* Point *past* the final NULL */ memcpy(buf, wrq_tail, sizeof wrq_tail); buf += sizeof wrq_tail; wrq_len = buf - wrq_packet_buf; timeout_ptr = TimeoutTable; /* Reset timeout */ sendreq: timeout = *timeout_ptr++; if (!timeout) return return_code; /* No file available... */ oldtime = jiffies(); core_udp_sendto(socket, wrq_packet_buf, wrq_len, url->ip, src_port); /* If the WRITE call fails, we let the timeout take care of it... */ for (;;) { buf_len = sizeof(reply_packet_buf); err = core_udp_recv(socket, reply_packet_buf, &buf_len, &src_ip, &src_port); if (err) { jiffies_t now = jiffies(); if (now - oldtime >= timeout) goto sendreq; } else { /* Make sure the packet actually came from the server and is long enough for a TFTP opcode */ dprintf("tftp_put: got packet buflen=%d from server %u.%u.%u.%u(%u.%u.%u.%u)\n", buf_len, ((uint8_t *)&src_ip)[0], ((uint8_t *)&src_ip)[1], ((uint8_t *)&src_ip)[2], ((uint8_t *)&src_ip)[3], ((uint8_t *)&url->ip)[0], ((uint8_t *)&url->ip)[1], ((uint8_t *)&url->ip)[2], ((uint8_t *)&url->ip)[3]); if ((src_ip == url->ip) && (buf_len >= 2)) break; } } core_udp_disconnect(socket); core_udp_connect(socket, src_ip, src_port); /* filesize <- -1 == unknown */ inode->size = -1; socket->tftp_blksize = TFTP_BLOCKSIZE; /* * Get the opcode type, and parse it */ opcode = *(uint16_t *)reply_packet_buf; switch (opcode) { case TFTP_ERROR: dprintf("tftp_push: received a TFTP_ERROR\n"); struct tftp_error *te = (struct tftp_error *)(reply_packet_buf+1); return_code = -ntohs(te->errcode); inode->size = 0; goto done; /* ERROR reply; don't try again */ case TFTP_ACK: dprintf("tftp_push: received a TFTP_ACK\n"); /* We received a ACK packet, sending the associated data packet */ /* If data was completly sent, we can stop here */ if (len == 0) { return_code = -ntohs(TFTP_OK); goto done; } /* If the server sequence is not aligned with our, we have an issue * Let's break the transmission for now but could be improved later */ uint16_t srv_seq = ntohs(*(uint16_t *)(reply_packet_buf+2)); if (srv_seq != seq) { printf("tftp_push: server sequence (%"PRIu16") is not aligned with our sequence (%"PRIu16"\n", srv_seq, seq); return_code = -ntohs(TFTP_EBADOP); goto done; } /* Let's transmit the data block */ chunk = len >= 512 ? 512 : len; buf = wrq_packet_buf; *(uint16_t *)buf = TFTP_DATA; /* TFTP opcode */ *((uint16_t *)(buf+2)) = htons(++seq); memcpy(buf+4, data, chunk); wrq_len = chunk + 4; data += chunk; len -= chunk; timeout_ptr = TimeoutTable; /* Reset timeout */ goto sendreq; default: dprintf("tftp_push: unknown opcode %d\n", ntohs(opcode)); return_code = -ntohs(TFTP_EOPTNEG); goto err_reply; } err_reply: /* Build the TFTP error packet */ dprintf("tftp_push: Failure\n"); tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error"); inode->size = 0; done: if (!inode->size) core_udp_close(socket); return return_code; }
/** * Open a TFTP connection to the server * * @param:inode, the inode to store our state in * @param:ip, the ip to contact to get the file * @param:filename, the file we wanna open * * @out: open_file_t structure, stores in file->open_file * @out: the lenght of this file, stores in file->file_len * */ void tftp_open(struct url_info *url, int flags, struct inode *inode, const char **redir) { struct pxe_pvt_inode *socket = PVT(inode); char *buf; uint16_t buf_len; char *p; char *options; char *data; static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408"; char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail]; char reply_packet_buf[PKTBUF_SIZE]; int err; int buffersize; int rrq_len; const uint8_t *timeout_ptr; jiffies_t timeout; jiffies_t oldtime; uint16_t opcode; uint16_t blk_num; uint64_t opdata; uint16_t src_port; uint32_t src_ip; (void)redir; /* TFTP does not redirect */ (void)flags; if (url->type != URL_OLD_TFTP) { /* * The TFTP URL specification allows the TFTP to end with a * ;mode= which we just ignore. */ url_unescape(url->path, ';'); } if (!url->port) url->port = TFTP_PORT; socket->ops = &tftp_conn_ops; if (core_udp_open(socket)) return; buf = rrq_packet_buf; *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ buf += 2; buf = stpcpy(buf, url->path); buf++; /* Point *past* the final NULL */ memcpy(buf, rrq_tail, sizeof rrq_tail); buf += sizeof rrq_tail; rrq_len = buf - rrq_packet_buf; timeout_ptr = TimeoutTable; /* Reset timeout */ sendreq: timeout = *timeout_ptr++; if (!timeout) return; /* No file available... */ oldtime = jiffies(); core_udp_sendto(socket, rrq_packet_buf, rrq_len, url->ip, url->port); /* If the WRITE call fails, we let the timeout take care of it... */ wait_pkt: for (;;) { buf_len = sizeof(reply_packet_buf); err = core_udp_recv(socket, reply_packet_buf, &buf_len, &src_ip, &src_port); if (err) { jiffies_t now = jiffies(); if (now - oldtime >= timeout) goto sendreq; } else { /* Make sure the packet actually came from the server and is long enough for a TFTP opcode */ dprintf("tftp_open: got packet buflen=%d from server %u.%u.%u.%u(%u.%u.%u.%u)\n", buf_len, ((uint8_t *)&src_ip)[0], ((uint8_t *)&src_ip)[1], ((uint8_t *)&src_ip)[2], ((uint8_t *)&src_ip)[3], ((uint8_t *)&url->ip)[0], ((uint8_t *)&url->ip)[1], ((uint8_t *)&url->ip)[2], ((uint8_t *)&url->ip)[3]); if ((src_ip == url->ip) && (buf_len >= 2)) break; } } core_udp_disconnect(socket); core_udp_connect(socket, src_ip, src_port); /* filesize <- -1 == unknown */ inode->size = -1; socket->tftp_blksize = TFTP_BLOCKSIZE; buffersize = buf_len - 2; /* bytes after opcode */ /* * Get the opcode type, and parse it */ opcode = *(uint16_t *)reply_packet_buf; switch (opcode) { case TFTP_ERROR: inode->size = 0; goto done; /* ERROR reply; don't try again */ case TFTP_DATA: /* * If the server doesn't support any options, we'll get a * DATA reply instead of OACK. Stash the data in the file * buffer and go with the default value for all options... * * We got a DATA packet, meaning no options are * suported. Save the data away and consider the * length undefined, *unless* this is the only * data packet... */ buffersize -= 2; if (buffersize < 0) goto wait_pkt; data = reply_packet_buf + 2; blk_num = ntohs(*(uint16_t *)data); data += 2; if (blk_num != 1) goto wait_pkt; socket->tftp_lastpkt = blk_num; if (buffersize > TFTP_BLOCKSIZE) goto err_reply; /* Corrupt */ socket->tftp_pktbuf = malloc(TFTP_BLOCKSIZE + 4); if (!socket->tftp_pktbuf) goto err_reply; /* Internal error */ if (buffersize < TFTP_BLOCKSIZE) { /* * This is the final EOF packet, already... * We know the filesize, but we also want to * ack the packet and set the EOF flag. */ inode->size = buffersize; socket->tftp_goteof = 1; ack_packet(inode, blk_num); } socket->tftp_bytesleft = buffersize; socket->tftp_dataptr = socket->tftp_pktbuf; memcpy(socket->tftp_pktbuf, data, buffersize); goto done; case TFTP_OACK: /* * Now we need to parse the OACK packet to get the transfer * and packet sizes. */ options = reply_packet_buf + 2; p = options; while (buffersize) { const char *opt = p; /* * If we find an option which starts with a NUL byte, * (a null option), we're either seeing garbage that some * TFTP servers add to the end of the packet, or we have * no clue how to parse the rest of the packet (what is * an option name and what is a value?) In either case, * discard the rest. */ if (!*opt) goto done; while (buffersize) { if (!*p) break; /* Found a final null */ *p++ |= 0x20; buffersize--; } if (!buffersize) break; /* Unterminated option */ /* Consume the terminal null */ p++; buffersize--; if (!buffersize) break; /* No option data */ opdata = 0; /* do convert a number-string to decimal number, just like atoi */ while (buffersize--) { uint8_t d = *p++; if (d == '\0') break; /* found a final null */ d -= '0'; if (d > 9) goto err_reply; /* Not a decimal digit */ opdata = opdata*10 + d; } if (!strcmp(opt, "tsize")) inode->size = opdata; else if (!strcmp(opt, "blksize")) socket->tftp_blksize = opdata; else goto err_reply; /* Non-negotitated option returned, no idea what it means ...*/ } if (socket->tftp_blksize < 64 || socket->tftp_blksize > PKTBUF_SIZE) goto err_reply; /* Parsing successful, allocate buffer */ socket->tftp_pktbuf = malloc(socket->tftp_blksize + 4); if (!socket->tftp_pktbuf) goto err_reply; else goto done; default: printf("TFTP unknown opcode %d\n", ntohs(opcode)); goto err_reply; } err_reply: /* Build the TFTP error packet */ tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error"); inode->size = 0; done: if (!inode->size) core_udp_close(socket); return; }
static int core_udp_listen(nc_sock_t *ncsock) { int ret, *sockbuf, sock, sock_max, timeout = ncsock->timeout; bool need_udphelper = TRUE; #ifdef USE_PKTINFO int sockopt = 1; #endif struct sockaddr_in myaddr; struct timeval tt; /* needed by the select() call */ debug_v(("core_udp_listen(ncsock=%p)", (void *)ncsock)); #ifdef USE_PKTINFO need_udphelper = FALSE; #else /* if we need a specified source address then go straight to it */ if (ncsock->local_host.iaddrs[0].s_addr) need_udphelper = FALSE; #endif if (!need_udphelper) { /* simulates a udphelper_sockets_open() call */ sockbuf = calloc(2, sizeof(int)); sockbuf[0] = 1; sockbuf[1] = sock = netcat_socket_new(PF_INET, SOCK_DGRAM); } #ifndef USE_PKTINFO else sock = udphelper_sockets_open(&sockbuf, ncsock->local_port.netnum); #endif if (sock < 0) goto err; /* we know that udphelper_sockets_open() returns the highest socket, and if we didn't call it we have just one socket */ sock_max = sock + 1; if (!need_udphelper) { /* prepare myaddr for the bind() call */ myaddr.sin_family = AF_INET; myaddr.sin_port = ncsock->local_port.netnum; memcpy(&myaddr.sin_addr, &ncsock->local_host.iaddrs[0], sizeof(myaddr.sin_addr)); /* bind() MUST be called in this function, since it's the final call for this type of socket. FIXME: I heard that UDP port 0 is illegal. true? */ ret = bind(sock, (struct sockaddr *)&myaddr, sizeof(myaddr)); if (ret < 0) goto err; } #ifdef USE_PKTINFO /* set the right flag in order to obtain the ancillary data */ ret = setsockopt(sock, SOL_IP, IP_PKTINFO, &sockopt, sizeof(sockopt)); if (ret < 0) goto err; #endif /* if the port was set to 0 this means that it is assigned randomly by the OS. Find out which port they assigned to us. */ if (ncsock->local_port.num == 0) { struct sockaddr_in get_myaddr; unsigned int get_myaddr_len = sizeof(get_myaddr); ret = getsockname(sock, (struct sockaddr *)&get_myaddr, &get_myaddr_len); if (ret < 0) goto err; netcat_getport(&ncsock->local_port, NULL, ntohs(get_myaddr.sin_port)); assert(ncsock->local_port.num != 0); } if (!need_udphelper) ncprint(NCPRINT_VERB2, _("Listening on %s"), netcat_strid(&ncsock->local_host, &ncsock->local_port)); else ncprint(NCPRINT_VERB2, _("Listening on %s (using %d sockets)"), netcat_strid(&ncsock->local_host, &ncsock->local_port), sockbuf[0]); /* since this protocol is connectionless, we need a special handling here. We want to simulate a two-ends connection but in order to do this we need a remote address and a local address (in case we bound to INADDR_ANY). Wait here until a packet is received, and use its source and destination addresses as default endpoints. If we have the zero-I/O option set, we just eat the packet and return when timeout is elapsed (maybe never). */ tt.tv_sec = timeout; tt.tv_usec = 0; while (TRUE) { int socks_loop; fd_set ins; FD_ZERO(&ins); for (socks_loop = 1; socks_loop <= sockbuf[0]; socks_loop++) { debug_v(("Setting sock %d on ins", sockbuf[socks_loop])); FD_SET(sockbuf[socks_loop], &ins); } /* automatically use remaining timeout time if in zero-I/O mode */ ret = select(sock_max, &ins, NULL, NULL, (timeout > 0 ? &tt : NULL)); if (ret == 0) break; /* loop all the open sockets to find the active one */ for (socks_loop = 1; socks_loop <= sockbuf[0]; socks_loop++) { int recv_ret, write_ret; struct msghdr my_hdr; #ifdef __MVS__ /* zosunix01 26.07.2011 */ unsigned char buf[32768]; #else unsigned char buf[1024]; #endif struct iovec my_hdr_vec; struct sockaddr_in rem_addr; struct sockaddr_in local_addr; #ifdef USE_PKTINFO unsigned char anc_buf[512]; #endif sock = sockbuf[socks_loop]; if (!FD_ISSET(sock, &ins)) continue; /* I've looked for this code for a lot of hours, and finally found the RFC 2292 which provides a socket API for fetching the destination interface of the incoming packet. */ memset(&my_hdr, 0, sizeof(my_hdr)); memset(&rem_addr, 0, sizeof(rem_addr)); memset(&local_addr, 0, sizeof(local_addr)); my_hdr.msg_name = (void *)&rem_addr; my_hdr.msg_namelen = sizeof(rem_addr); /* initialize the vector struct and then the vectory member of the header */ my_hdr_vec.iov_base = buf; my_hdr_vec.iov_len = sizeof(buf); my_hdr.msg_iov = &my_hdr_vec; my_hdr.msg_iovlen = 1; #ifdef USE_PKTINFO /* now the core part for the IP_PKTINFO support: the ancillary data */ my_hdr.msg_control = anc_buf; my_hdr.msg_controllen = sizeof(anc_buf); #endif /* now check the remote address. If we are simulating a routing then use the MSG_PEEK flag, which leaves the received packet untouched */ recv_ret = recvmsg(sock, &my_hdr, (opt_zero ? 0 : MSG_PEEK)); debug_v(("received packet from %s:%d%s", netcat_inet_ntop(&rem_addr.sin_addr), ntohs(rem_addr.sin_port), (opt_zero ? "" : ", using as default dest"))); #ifdef USE_PKTINFO ret = udphelper_ancillary_read(&my_hdr, &local_addr); local_addr.sin_port = myaddr.sin_port; local_addr.sin_family = myaddr.sin_family; #else ret = sizeof(local_addr); ret = getsockname(sock, (struct sockaddr *)&local_addr, &ret); #endif if (ret == 0) { char tmpbuf[127]; strncpy(tmpbuf, netcat_inet_ntop(&rem_addr.sin_addr), sizeof(tmpbuf)); ncprint(NCPRINT_VERB1, _("Received packet from %s:%d -> %s:%d (local)"), tmpbuf, ntohs(rem_addr.sin_port), netcat_inet_ntop(&local_addr.sin_addr), ntohs(local_addr.sin_port)); } else ncprint(NCPRINT_VERB1, _("Received packet from %s:%d"), netcat_inet_ntop(&rem_addr.sin_addr), ntohs(rem_addr.sin_port)); if (opt_zero) { /* output the packet right here right now */ write_ret = write(STDOUT_FILENO, buf, recv_ret); bytes_recv += write_ret; debug_dv(("write_u(stdout) = %d", write_ret)); if (write_ret < 0) { perror("write_u(stdout)"); exit(EXIT_FAILURE); } /* FIXME: unhandled exception */ assert(write_ret == recv_ret); /* if the hexdump option is set, hexdump the received data */ if (opt_hexdump) { #ifndef USE_OLD_HEXDUMP fprintf(output_fp, "Received %d bytes from %s:%d\n", recv_ret, netcat_inet_ntop(&rem_addr.sin_addr), ntohs(rem_addr.sin_port)); #endif netcat_fhexdump(output_fp, '<', buf, write_ret); } } else { #ifdef USE_PKTINFO nc_sock_t dup_socket; memset(&dup_socket, 0, sizeof(dup_socket)); dup_socket.domain = ncsock->domain; dup_socket.proto = ncsock->proto; memcpy(&dup_socket.local_host.iaddrs[0], &local_addr.sin_addr, sizeof(local_addr)); memcpy(&dup_socket.host.iaddrs[0], &rem_addr.sin_addr, sizeof(local_addr)); dup_socket.local_port.netnum = local_addr.sin_port; dup_socket.local_port.num = ntohs(local_addr.sin_port); dup_socket.port.netnum = rem_addr.sin_port; dup_socket.port.num = ntohs(rem_addr.sin_port); /* copy the received data in the socket's queue */ ncsock->recvq.len = recv_ret; ncsock->recvq.head = ncsock->recvq.pos = malloc(recv_ret); memcpy(ncsock->recvq.head, my_hdr_vec.iov_base, recv_ret); /* FIXME: this ONLY saves the first 1024 bytes! and the others? */ #else ret = connect(sock, (struct sockaddr *)&rem_addr, sizeof(rem_addr)); if (ret < 0) goto err; /* remove this socket from the array in order not to get it closed */ sockbuf[socks_loop] = -1; #endif udphelper_sockets_close(sockbuf); #ifdef USE_PKTINFO /* this is all we want from this function */ debug_dv(("calling the udp_connect() function...")); return core_udp_connect(&dup_socket); #else return sock; #endif } } /* end of foreach (sock, sockbuf) */ } /* end of packet receiving loop */ /* no packets until timeout, set errno and proceed to general error handling */ errno = ETIMEDOUT; err: udphelper_sockets_close(sockbuf); return -1; } /* end of core_udp_listen() */