// THIS NEEDS TO BE CHECKED FOR BUFFER OVERFLOWS // IMPORTANT!!!! int handle_firmware(node n, artnet_packet p) { int length, offset, block_length, total_blocks, block_id; artnet_firmware_status_code response_code = ARTNET_FIRMWARE_FAIL; // run callback if defined if (check_callback(n, p, n->callbacks.firmware)) return ARTNET_EOK; /* * What happens if an upload is less than 512 bytes ????? */ if ( p->data.firmware.type == ARTNET_FIRMWARE_FIRMFIRST || p->data.firmware.type == ARTNET_FIRMWARE_UBEAFIRST) { // a new transfer is initiated if (n->firmware.peer.s_addr == 0) { //new transfer // these are 2 byte words, so we get a total of 1k of data per packet length = artnet_misc_nbytes_to_32( p->data.firmware.length ) * sizeof(p->data.firmware.data[0]); // set parameters n->firmware.peer.s_addr = p->from.s_addr; n->firmware.data = malloc(length); if (n->firmware.data == NULL) { artnet_error_malloc(); return ARTNET_EMEM; } n->firmware.bytes_total = length; n->firmware.last_time = time(NULL); n->firmware.expected_block = 1; // check if this is a ubea upload or not if (p->data.firmware.type == ARTNET_FIRMWARE_FIRMFIRST) n->firmware.ubea = 0; else n->firmware.ubea = 1; // take the minimum of the total length and the max packet size block_length = min((unsigned int) length, ARTNET_FIRMWARE_SIZE * sizeof(p->data.firmware.data[0])); memcpy(n->firmware.data, p->data.firmware.data, block_length); n->firmware.bytes_current = block_length; if (block_length == length) { // this is the first and last packet // upload was less than 1k bytes // this behaviour isn't in the spec, presumably no firmware will be less that 1k response_code = ARTNET_FIRMWARE_ALLGOOD; // do the callback here if (n->callbacks.firmware_c.fh != NULL) n->callbacks.firmware_c.fh(n, n->firmware.ubea, n->firmware.data, n->firmware.bytes_total, n->callbacks.firmware_c.data); } else { response_code = ARTNET_FIRMWARE_BLOCKGOOD; } } else { // already in a transfer printf("First, but already for a packet\n"); // send a failure response_code = ARTNET_FIRMWARE_FAIL; } } else if (p->data.firmware.type == ARTNET_FIRMWARE_FIRMCONT || p->data.firmware.type == ARTNET_FIRMWARE_UBEACONT) { // continued transfer length = artnet_misc_nbytes_to_32(p->data.firmware.length) * sizeof(p->data.firmware.data[0]); total_blocks = length / ARTNET_FIRMWARE_SIZE / 2 + 1; block_length = ARTNET_FIRMWARE_SIZE * sizeof(uint16_t); block_id = p->data.firmware.blockId; // ok the blockid field is only 1 byte, so it wraps back to 0x00 we // need to watch for this if (n->firmware.expected_block > UINT8_MAX && (n->firmware.expected_block % (UINT8_MAX+1)) == p->data.firmware.blockId) { block_id = n->firmware.expected_block; } offset = block_id * ARTNET_FIRMWARE_SIZE; if (n->firmware.peer.s_addr == p->from.s_addr && length == n->firmware.bytes_total && block_id < total_blocks-1) { memcpy(n->firmware.data + offset, p->data.firmware.data, block_length); n->firmware.bytes_current += block_length; n->firmware.expected_block++; response_code = ARTNET_FIRMWARE_BLOCKGOOD; } else { printf("cont, ips don't match or length has changed or out of range block num\n" ); // in a transfer not from this ip response_code = ARTNET_FIRMWARE_FAIL; } } else if (p->data.firmware.type == ARTNET_FIRMWARE_FIRMLAST || p->data.firmware.type == ARTNET_FIRMWARE_UBEALAST) { length = artnet_misc_nbytes_to_32( p->data.firmware.length) * sizeof(p->data.firmware.data[0]); total_blocks = length / ARTNET_FIRMWARE_SIZE / 2 + 1; // length should be the remaining data block_length = n->firmware.bytes_total % (ARTNET_FIRMWARE_SIZE * sizeof(uint16_t)); block_id = p->data.firmware.blockId; // ok the blockid field is only 1 byte, so it wraps back to 0x00 we // need to watch for this if (n->firmware.expected_block > UINT8_MAX && (n->firmware.expected_block % (UINT8_MAX+1)) == p->data.firmware.blockId) { block_id = n->firmware.expected_block; } offset = block_id * ARTNET_FIRMWARE_SIZE; if (n->firmware.peer.s_addr == p->from.s_addr && length == n->firmware.bytes_total && block_id == total_blocks-1) { // all the checks work out memcpy(n->firmware.data + offset, p->data.firmware.data, block_length); n->firmware.bytes_current += block_length; // do the callback here if (n->callbacks.firmware_c.fh != NULL) n->callbacks.firmware_c.fh(n, n->firmware.ubea, n->firmware.data, n->firmware.bytes_total / sizeof(p->data.firmware.data[0]), n->callbacks.firmware_c.data); // reset values and free reset_firmware_upload(n); response_code = ARTNET_FIRMWARE_ALLGOOD; printf("Firmware upload complete\n"); } else if (n->firmware.peer.s_addr != p->from.s_addr) { // in a transfer not from this ip printf("last, ips don't match\n" ); response_code = ARTNET_FIRMWARE_FAIL; } else if (length != n->firmware.bytes_total) { // they changed the length mid way thru a transfer printf("last, lengths have changed %d %d\n", length, n->firmware.bytes_total); response_code = ARTNET_FIRMWARE_FAIL; } else if (block_id != total_blocks -1) { // the blocks don't match up printf("This is the last block, but not according to the lengths %d %d\n", block_id, total_blocks -1); response_code = ARTNET_FIRMWARE_FAIL; } } return artnet_tx_firmware_reply(n, p->from.s_addr, response_code); }
/* * Set if_head to point to a list of iface_t structures which represent the * interfaces on this machine * @param ift_head the address of the pointer to the head of the list */ static int get_ifaces(iface_t **if_head) { struct ifconf ifc; struct ifreq *ifr, ifrcopy; struct sockaddr_in *sin; int len, lastlen, flags; char *buf, *ptr; iface_t *if_tail, *iface; int ret = ARTNET_EOK; int sd; *if_head = if_tail = NULL; // create socket to get iface config sd = socket(PF_INET, SOCK_DGRAM, 0); if (sd < 0) { artnet_error("%s : Could not create socket %s", __FUNCTION__, strerror(errno)); ret = ARTNET_ENET; goto e_return; } // first use ioctl to get a listing of interfaces lastlen = 0; len = INITIAL_IFACE_COUNT * sizeof(struct ifreq); for (;;) { buf = malloc(len); if (buf == NULL) { artnet_error_malloc(); ret = ARTNET_EMEM; goto e_free; } ifc.ifc_len = len; ifc.ifc_buf = buf; if (ioctl(sd, SIOCGIFCONF, &ifc) < 0) { if (errno != EINVAL || lastlen != 0) { artnet_error("%s : ioctl error %s", __FUNCTION__, strerror(errno)); ret = ARTNET_ENET; goto e_free; } } else { if (ifc.ifc_len == lastlen) break; lastlen = ifc.ifc_len; } len += IFACE_COUNT_INC * sizeof(struct ifreq); free(buf); } // loop through each iface for (ptr = buf; ptr < buf + ifc.ifc_len;) { ifr = (struct ifreq*) ptr; // work out length here #ifdef HAVE_SOCKADDR_SA_LEN len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len); #else switch (ifr->ifr_addr.sa_family) { #ifdef IPV6 case AF_INET6: len = sizeof(struct sockaddr_in6); break; #endif case AF_INET: default: len = sizeof(SA); break; } #endif ptr += sizeof(ifr->ifr_name) + len; // look for AF_INET interfaces if (ifr->ifr_addr.sa_family == AF_INET) { ifrcopy = *ifr; if (ioctl(sd, SIOCGIFFLAGS, &ifrcopy) < 0) { artnet_error("%s : ioctl error %s" , __FUNCTION__, strerror(errno)); ret = ARTNET_ENET; goto e_free_list; } flags = ifrcopy.ifr_flags; if ((flags & IFF_UP) == 0) continue; //skip down interfaces if ((flags & IFF_LOOPBACK)) continue; //skip lookback iface = new_iface(if_head, &if_tail); if (!iface) goto e_free_list; sin = (struct sockaddr_in *) &ifr->ifr_addr; iface->ip_addr.sin_addr = sin->sin_addr; // fetch bcast address #ifdef SIOCGIFBRDADDR if (flags & IFF_BROADCAST) { if (ioctl(sd, SIOCGIFBRDADDR, &ifrcopy) < 0) { artnet_error("%s : ioctl error %s" , __FUNCTION__, strerror(errno)); ret = ARTNET_ENET; goto e_free_list; } sin = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; iface->bcast_addr.sin_addr = sin->sin_addr; } #endif // fetch hardware address #ifdef SIOCGIFHWADDR if (flags & SIOCGIFHWADDR) { if (ioctl(sd, SIOCGIFHWADDR, &ifrcopy) < 0) { artnet_error("%s : ioctl error %s", __FUNCTION__, strerror(errno)); ret = ARTNET_ENET; goto e_free_list; } memcpy(&iface->hw_addr, ifrcopy.ifr_hwaddr.sa_data, ARTNET_MAC_SIZE); } #endif /* ok, if that all failed we should prob try and use sysctl to work out the bcast * and hware addresses * i'll leave that for another day */ } } free(buf); return ARTNET_EOK; e_free_list: free_ifaces(*if_head); e_free: free(buf); close(sd); e_return: return ret; }