struct pico_device *pico_vde_create(char *sock, char *name, uint8_t *mac) { struct pico_device_vde *vde = pico_zalloc(sizeof(struct pico_device_vde)); struct vde_open_args open_args = { .mode = 0700 }; char vdename[] = "picotcp"; if (!vde) return NULL; if( 0 != pico_device_init((struct pico_device *)vde, name, mac)) { dbg ("Vde init failed.\n"); pico_vde_destroy((struct pico_device *)vde); return NULL; } vde->dev.overhead = 0; vde->sock = pico_zalloc(strlen(sock) + 1); memcpy(vde->sock, sock, strlen(sock)); vde->conn = vde_open(sock, vdename, &open_args); if (!vde->conn) { pico_vde_destroy((struct pico_device *)vde); return NULL; } vde->dev.send = pico_vde_send; vde->dev.poll = pico_vde_poll; vde->dev.destroy = pico_vde_destroy; dbg("Device %s created.\n", vde->dev.name); return (struct pico_device *)vde; }
static struct pico_dns_query *pico_dns_client_add_query(struct pico_dns_prefix *hdr, uint16_t len, struct pico_dns_query_suffix *suffix, void (*callback)(char *, void *), void *arg) { struct pico_dns_query *q = NULL, *found = NULL; q = pico_zalloc(sizeof(struct pico_dns_query)); if (!q) return NULL; q->query = (char *)hdr; q->len = len; q->id = short_be(hdr->id); q->qtype = short_be(suffix->qtype); q->qclass = short_be(suffix->qclass); q->retrans = 1; q->q_ns = *((struct pico_dns_ns *)pico_tree_first(&NSTable)); q->callback = callback; q->arg = arg; q->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dns_client_callback); if (!q->s) { pico_free(q); return NULL; } found = pico_tree_insert(&DNSTable, q); if (found) { pico_err = PICO_ERR_EAGAIN; pico_socket_close(q->s); pico_free(q); return NULL; } return q; }
static struct pico_dns_ns *pico_dns_client_add_ns(struct pico_ip4 *ns_addr) { struct pico_dns_ns *dns = NULL, *found = NULL, test = {{0}}; dns = pico_zalloc(sizeof(struct pico_dns_ns)); if (!dns) { pico_err = PICO_ERR_ENOMEM; return NULL; } dns->ns = *ns_addr; found = pico_tree_insert(&NSTable, dns); if (found) { /* nameserver already present */ pico_free(dns); return found; } /* default NS found, remove it */ pico_string_to_ipv4(PICO_DNS_NS_GOOGLE, &test.ns.addr); found = pico_tree_findKey(&NSTable, &test); if (found && (found->ns.addr != ns_addr->addr)) pico_dns_client_del_ns(&found->ns); return dns; }
static struct pico_dhcp_client_cookie *pico_dhcp_client_add_cookie(uint32_t xid, struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid) { struct pico_dhcp_client_cookie *dhcpc = NULL, *found = NULL, test = { 0 }; test.xid = xid; found = pico_tree_findKey(&DHCPCookies, &test); if (found) { pico_err = PICO_ERR_EAGAIN; return NULL; } dhcpc = pico_zalloc(sizeof(struct pico_dhcp_client_cookie)); if (!dhcpc) { pico_err = PICO_ERR_ENOMEM; return NULL; } dhcpc->state = DHCP_CLIENT_STATE_INIT; dhcpc->status = DHCP_CLIENT_STATUS_INIT; dhcpc->xid = xid; dhcpc->uid = uid; *(dhcpc->uid) = 0; dhcpc->cb = cb; dhcpc->dev = dev; pico_tree_insert(&DHCPCookies, dhcpc); return dhcpc; }
static struct pico_dhcp_server_setting *pico_dhcp_server_add_setting(struct pico_dhcp_server_setting *setting) { uint16_t port = PICO_DHCPD_PORT; struct pico_dhcp_server_setting *dhcps = NULL, *found = NULL, test = {0}; struct pico_ipv4_link *link = NULL; link = pico_ipv4_link_get(&setting->server_ip); if (!link) { pico_err = PICO_ERR_EINVAL; return NULL; } test.dev = setting->dev; found = pico_tree_findKey(&DHCPSettings, &test); if (found) { pico_err = PICO_ERR_EINVAL; return NULL; } dhcps = pico_zalloc(sizeof(struct pico_dhcp_server_setting)); if (!dhcps) { pico_err = PICO_ERR_ENOMEM; return NULL; } dhcps->lease_time = setting->lease_time; dhcps->pool_start = setting->pool_start; dhcps->pool_next = setting->pool_next; dhcps->pool_end = setting->pool_end; dhcps->dev = link->dev; dhcps->server_ip = link->address; dhcps->netmask = link->netmask; /* default values if not provided */ if (!dhcps->pool_start) dhcps->pool_start = (dhcps->server_ip.addr & dhcps->netmask.addr) | DHCP_SERVER_POOL_START; if (!dhcps->pool_end) dhcps->pool_end = (dhcps->server_ip.addr & dhcps->netmask.addr) | DHCP_SERVER_POOL_END; if (!dhcps->lease_time) dhcps->lease_time = DHCP_SERVER_LEASE_TIME; dhcps->pool_next = dhcps->pool_start; dhcps->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcpd_wakeup); if (!dhcps->s) { dhcps_dbg("DHCP server ERROR: failure opening socket (%s)\n", strerror(pico_err)); pico_free(dhcps); return NULL; } if (pico_socket_bind(dhcps->s, &dhcps->server_ip, &port) < 0) { dhcps_dbg("DHCP server ERROR: failure binding socket (%s)\n", strerror(pico_err)); pico_free(dhcps); return NULL; } pico_tree_insert(&DHCPSettings, dhcps); return dhcps; }
static void next_ping(uint32_t now, void *arg) { struct pico_icmp4_ping_cookie *newcookie, *cookie = (struct pico_icmp4_ping_cookie *)arg; IGNORE_PARAMETER(now); if(pico_tree_findKey(&Pings,cookie)){ if (cookie->seq < cookie->count) { newcookie = pico_zalloc(sizeof(struct pico_icmp4_ping_cookie)); if (!newcookie) return; memcpy(newcookie, cookie, sizeof(struct pico_icmp4_ping_cookie)); newcookie->seq++; pico_tree_insert(&Pings,newcookie); send_ping(newcookie); } } }
static struct pico_dhcp_server_negotiation *pico_dhcp_server_add_negotiation(struct pico_device *dev, struct pico_dhcp_hdr *hdr) { struct pico_dhcp_server_negotiation *dhcpn = NULL; struct pico_dhcp_server_setting test = {0}; struct pico_ip4 *ciaddr = NULL; if (pico_dhcp_server_find_negotiation(hdr->xid)) return NULL; dhcpn = pico_zalloc(sizeof(struct pico_dhcp_server_negotiation)); if (!dhcpn) { pico_err = PICO_ERR_ENOMEM; return NULL; } dhcpn->xid = hdr->xid; dhcpn->state = PICO_DHCP_STATE_DISCOVER; memcpy(dhcpn->hwaddr.addr, hdr->hwaddr, PICO_SIZE_ETH); test.dev = dev; dhcpn->dhcps = pico_tree_findKey(&DHCPSettings, &test); if (!dhcpn->dhcps) { dhcps_dbg("DHCP server WARNING: received DHCP message on unconfigured link %s\n", dev->name); pico_free(dhcpn); return NULL; } ciaddr = pico_arp_reverse_lookup(&dhcpn->hwaddr); if (!ciaddr) { dhcpn->ciaddr.addr = dhcpn->dhcps->pool_next; dhcpn->dhcps->pool_next = long_be(long_be(dhcpn->dhcps->pool_next) + 1); pico_arp_create_entry(dhcpn->hwaddr.addr, dhcpn->ciaddr, dhcpn->dhcps->dev); } else { dhcpn->ciaddr = *ciaddr; } pico_tree_insert(&DHCPNegotiations, dhcpn); return dhcpn; }
int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *)) { static uint16_t next_id = 0x91c0; struct pico_icmp4_ping_cookie *cookie; if((dst == NULL) || (interval == 0) || (timeout == 0) || (count == 0)){ pico_err = PICO_ERR_EINVAL; return -1; } cookie = pico_zalloc(sizeof(struct pico_icmp4_ping_cookie)); if (!cookie) { pico_err = PICO_ERR_ENOMEM; return -1; } if (pico_string_to_ipv4(dst, &cookie->dst.addr) < 0) { pico_err = PICO_ERR_EINVAL; pico_free(cookie); return -1; } cookie->seq = 1; cookie->id = next_id++; cookie->err = PICO_PING_ERR_PENDING; cookie->size = (uint16_t)size; cookie->interval = interval; cookie->timeout = timeout; cookie->cb = cb; cookie->count = count; pico_tree_insert(&Pings,cookie); send_ping(cookie); return 0; }
void serverWakeup(uint16_t ev, uint16_t conn) { static FILE *f; char buffer[SIZE]; if(ev & EV_HTTP_CON) { printf("New connection received....\n"); pico_http_server_accept(); } if(ev & EV_HTTP_REQ) /* new header received */ { uint16_t read; char *resource; int method; printf("Header request was received...\n"); printf("> Resource : %s\n", pico_http_getResource(conn)); resource = pico_http_getResource(conn); method = pico_http_getMethod(conn); if(strcmp(resource, "/") == 0 || strcmp(resource, "index.html") == 0 || strcmp(resource, "/index.html") == 0) { if(method == HTTP_METHOD_GET) { /* Accepting request */ printf("Received GET request\n"); pico_http_respond(conn, HTTP_RESOURCE_FOUND); f = fopen("test/examples/form.html", "r"); if(!f) { fprintf(stderr, "Unable to open the file /test/examples/form.html\n"); } read = (uint16_t)fread(buffer, 1u, SIZE, f); pico_http_submitData(conn, buffer, read); } else if(method == HTTP_METHOD_POST) { printf("Received POST request\n"); printf("Form fields: %s\n", pico_http_getBody(conn)); pico_http_respond(conn, HTTP_RESOURCE_FOUND); strcpy(buffer, "Thanks for posting your data"); if(pico_http_submitData(conn, buffer, (uint16_t)strlen(buffer)) == HTTP_RETURN_ERROR) { printf("error submitting data\n"); } else { printf("data submitted correctly\n"); } } } else if(strcmp(resource, "/download") == 0) { const char download_url_field [] = "download_url="; char *download_url = NULL; char *download_basename = NULL; char *decoded_download_url = NULL; char *http_body = NULL; http_body = pico_http_getBody(conn); if(http_body != NULL) { download_url = strstr(http_body, download_url_field); if(download_url != NULL) { download_url = download_url + strlen(download_url_field); decoded_download_url = pico_zalloc(strlen(download_url) + 1); url_decode(decoded_download_url, download_url); printf("Download url: %s\n", decoded_download_url); if(pico_http_client_open(decoded_download_url, wget_callback) < 0) { fprintf(stderr, " error opening the url : %s, please check the format\n", decoded_download_url); pico_http_respond(conn, HTTP_RESOURCE_NOT_FOUND); } download_basename = basename(decoded_download_url); url_filename = pico_zalloc(strlen(download_basename) + 1); strcpy(url_filename, download_basename); pico_free(decoded_download_url); pico_http_respond(conn, HTTP_RESOURCE_FOUND); strcpy(buffer, "Download started"); if(pico_http_submitData(conn, buffer, (uint16_t)strlen(buffer)) == HTTP_RETURN_ERROR) { printf("error submitting data\n"); } } else { printf("no download url\n"); pico_http_respond(conn, HTTP_RESOURCE_NOT_FOUND); } } else { printf("no http body\n"); pico_http_respond(conn, HTTP_RESOURCE_NOT_FOUND); } } else { /* reject */ printf("Rejected connection...\n"); pico_http_respond(conn, HTTP_RESOURCE_NOT_FOUND); } } if(ev & EV_HTTP_PROGRESS) /* submitted data was sent */ { uint16_t sent, total; pico_http_getProgress(conn, &sent, &total); printf("Chunk statistics : %d/%d sent\n", sent, total); } if(ev & EV_HTTP_SENT) /* submitted data was fully sent */ { int method; method = pico_http_getMethod(conn); if(method == HTTP_METHOD_GET) { uint16_t read; read = (uint16_t)fread(buffer, 1, SIZE, f); printf("Chunk was sent...\n"); if(read > 0) { printf("Sending another chunk...\n"); pico_http_submitData(conn, buffer, read); } else { printf("Last chunk get !\n"); pico_http_submitData(conn, NULL, 0); /* send the final chunk */ fclose(f); } } else if(method == HTTP_METHOD_POST) { printf("Last chunk post !\n"); pico_http_submitData(conn, NULL, 0); /* send the final chunk */ } } if(ev & EV_HTTP_CLOSE) { printf("Close request...\n"); pico_http_close(conn); } if(ev & EV_HTTP_ERROR) { printf("Error on server...\n"); pico_http_close(conn); } }
static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s) { char *q_qname, *q_suf, *a_hdr, *a_qname, *a_suf, *a_rdata; struct dns_message_hdr *hdr; struct dns_query_suffix query_suf; struct dns_answer_suffix answer_suf; struct pico_dns_key test, *key; char *answer; char dns_answer[PICO_DNS_MAX_RESPONSE_LEN] = {0}; uint8_t valid_suffix = 0; uint16_t compression = 0; int i = 0, r = 0; if (ev & PICO_SOCK_EV_RD) { r = pico_socket_read(s, dns_answer, PICO_DNS_MAX_RESPONSE_LEN); pico_socket_close(s); if (r == PICO_DNS_MAX_RESPONSE_LEN || r < (int)sizeof(struct dns_message_hdr)) { dns_dbg("DNS ERROR: received incorrect number(%d) of bytes\n", r); return; } /* Check header validity */ a_hdr = dns_answer; hdr = (struct dns_message_hdr *) a_hdr; pico_dns_client_hdr_ntoh(hdr); if (GET_FLAG_QR(hdr) != PICO_DNS_QR_RESPONSE || GET_FLAG_OPCODE(hdr) != PICO_DNS_OPCODE_QUERY || GET_FLAG_TC(hdr) == PICO_DNS_TC_IS_TRUNCATED || GET_FLAG_RCODE(hdr) != PICO_DNS_RCODE_NO_ERROR) { dns_dbg("DNS ERROR: OPCODE %d | TC %d | RCODE %d\n", GET_FLAG_OPCODE(hdr), GET_FLAG_TC(hdr), GET_FLAG_RCODE(hdr)); return; } if (hdr->ancount < 1 || r < (int)(sizeof(struct dns_message_hdr) + hdr->qdcount * sizeof(struct dns_query_suffix) + hdr->ancount * sizeof(struct dns_answer_suffix))) { dns_dbg("DNS ERROR: ancount < 1 OR received number(%d) of bytes too low\n", r); return; } /* Find DNS key */ test.id = hdr->id; key = pico_tree_findKey(&DNSTable,&test); if (!key) { dns_dbg("DNS WARNING: key with id %u not found\n", hdr->id); return; } key->retrans = 0; /* Check query suffix validity */ q_qname = a_hdr + sizeof(struct dns_message_hdr); q_suf = pico_dns_client_seek(q_qname); query_suf = *(struct dns_query_suffix *) q_suf; if (short_be(query_suf.qtype) != key->qtype || short_be(query_suf.qclass) != key->qclass) { dns_dbg("DNS ERROR: received qtype (%u) or qclass (%u) incorrect\n", short_be(query_suf.qtype), short_be(query_suf.qclass)); return; } /* Seek answer suffix */ a_qname = q_suf + sizeof(struct dns_query_suffix); a_suf = a_qname; while(i++ < hdr->ancount) { uint16_t comp_h = short_from(a_suf); compression = short_be(comp_h); switch (compression >> 14) { case PICO_DNS_POINTER: while (compression >> 14 == PICO_DNS_POINTER) { dns_dbg("DNS: pointer\n"); a_suf += sizeof(uint16_t); comp_h = short_from(a_suf); compression = short_be(comp_h); } break; case PICO_DNS_LABEL: dns_dbg("DNS: label\n"); a_suf = pico_dns_client_seek(a_qname); break; default: dns_dbg("DNS ERROR: incorrect compression (%u) value\n", compression); return; } /* Check answer suffix validity */ answer_suf = *(struct dns_answer_suffix *)a_suf; if (short_be(answer_suf.qtype) != key->qtype || short_be(answer_suf.qclass) != key->qclass) { dns_dbg("DNS WARNING: received qtype (%u) or qclass (%u) incorrect\n", short_be(answer_suf.qtype), short_be(answer_suf.qclass)); a_suf = a_suf + sizeof(struct dns_answer_suffix) + short_be(answer_suf.rdlength); continue; } if (short_be(answer_suf.ttl) > PICO_DNS_MAX_TTL) { dns_dbg("DNS WARNING: received TTL (%u) > MAX (%u)\n", short_be(answer_suf.ttl), PICO_DNS_MAX_TTL); a_suf = a_suf + sizeof(struct dns_answer_suffix) + short_be(answer_suf.rdlength); continue; } valid_suffix = 1; break; } if (!valid_suffix) { dns_dbg("DNS ERROR: invalid dns answer suffix\n"); return; } a_rdata = a_suf + sizeof(struct dns_answer_suffix); if (key->qtype == PICO_DNS_TYPE_A) { uint32_t ip_h = long_from(a_rdata); dns_dbg("DNS: length %u | ip %08X\n", short_be(answer_suf.rdlength), long_be(ip_h)); answer = pico_zalloc(16); pico_ipv4_to_string(answer, ip_h); key->callback(answer, key->arg); } else if (key->qtype == PICO_DNS_TYPE_PTR) { pico_dns_client_reverse_label((char *) a_rdata); dns_dbg("DNS: length %u | name %s\n", short_be(answer_suf.rdlength), (char *)a_rdata + 1); answer = pico_zalloc(answer_suf.rdlength - 1); memcpy(answer, (char *)a_rdata + 1, short_be(answer_suf.rdlength) - 1); key->callback(answer, key->arg); } else { dns_dbg("DNS ERROR: incorrect qtype (%u)\n", key->qtype); return; } }
int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag) { struct pico_dns_ns test, *key = NULL; if (!ns) { pico_err = PICO_ERR_EINVAL; return -1; } switch (flag) { case PICO_DNS_NS_ADD: key = pico_zalloc(sizeof(struct pico_dns_ns)); if (!key) { pico_err = PICO_ERR_ENOMEM; return -1; } key->ns = *ns; if(pico_tree_insert(&NSTable,key)){ dns_dbg("DNS WARNING: nameserver %08X already added\n",ns->addr); pico_err = PICO_ERR_EINVAL; pico_free(key); return -1; /* Element key already exists */ } dns_dbg("DNS: nameserver %08X added\n", ns->addr); /* If default NS found, remove it */ pico_string_to_ipv4(PICO_DNS_NS_GOOGLE, &test.ns.addr); if (ns->addr != test.ns.addr) { key = pico_tree_findKey(&NSTable,&test); if (key) { if(pico_tree_delete(&NSTable,key)) { dns_dbg("DNS: default nameserver %08X removed\n", test.ns.addr); pico_free(key); } else { pico_err = PICO_ERR_EAGAIN; return -1; } } } break; case PICO_DNS_NS_DEL: test.ns = *ns; key = pico_tree_findKey(&NSTable,&test); if (!key) { dns_dbg("DNS WARNING: nameserver %08X not found\n", ns->addr); pico_err = PICO_ERR_EINVAL; return -1; } /* RB_REMOVE returns pointer to removed element, NULL to indicate error */ if(pico_tree_delete(&NSTable,key)) { dns_dbg("DNS: nameserver %08X removed\n",key->ns.addr); pico_free(key); } else { pico_err = PICO_ERR_EAGAIN; return -1; } /* If no NS left, add default NS */ if(pico_tree_first(&NSTable) == NULL){ dns_dbg("DNS: add default nameserver\n"); return pico_dns_client_init(); } break; default: pico_err = PICO_ERR_EINVAL; return -1; } return 0; }
int8_t pico_processURI(const char * uri, struct pico_http_uri * urikey) { uint16_t lastIndex = 0, index; if(!uri || !urikey || uri[0] == '/') { pico_err = PICO_ERR_EINVAL; goto error; } // detect protocol => search for "://" if(memcmp(uri,HTTP_PROTO_TOK,HTTP_PROTO_LEN) == 0) // could be optimized { // protocol identified, it is http urikey->protoHttp = TRUE; lastIndex = HTTP_PROTO_LEN; } else { if(strstr(uri,"://")) // different protocol specified { urikey->protoHttp = FALSE; goto error; } // no protocol specified, assuming by default it's http urikey->protoHttp = TRUE; } // detect hostname index = lastIndex; while(uri[index] && uri[index]!='/' && uri[index]!=':') index++; if(index == lastIndex) { // wrong format urikey->host = urikey->resource = NULL; urikey->port = urikey->protoHttp = 0u; goto error; } else { // extract host urikey->host = (char *)pico_zalloc((uint32_t)(index-lastIndex+1)); if(!urikey->host) { // no memory goto error; } memcpy(urikey->host,uri+lastIndex,(size_t)(index-lastIndex)); } if(!uri[index]) { // nothing specified urikey->port = 80u; urikey->resource = pico_zalloc(2u); urikey->resource[0] = '/'; return HTTP_RETURN_OK; } else if(uri[index] == '/') { urikey->port = 80u; } else if(uri[index] == ':') { urikey->port = 0u; index++; while(uri[index] && uri[index]!='/') { // should check if every component is a digit urikey->port = (uint16_t)(urikey->port*10 + (uri[index] - '0')); index++; } } // extract resource if(!uri[index]) { urikey->resource = pico_zalloc(2u); urikey->resource[0] = '/'; } else { lastIndex = index; while(uri[index] && uri[index]!='?' && uri[index]!='&' && uri[index]!='#') index++; urikey->resource = (char *)pico_zalloc((size_t)(index-lastIndex+1)); if(!urikey->resource) { // no memory pico_err = PICO_ERR_ENOMEM; goto error; } memcpy(urikey->resource,uri+lastIndex,(size_t)(index-lastIndex)); } return HTTP_RETURN_OK; error : if(urikey->resource) { pico_free(urikey->resource); urikey->resource = NULL; } if(urikey->host) { pico_free(urikey->host); urikey->host = NULL; } return HTTP_RETURN_ERROR; }
static void dhcpd_make_reply(struct pico_dhcp_server_negotiation *dhcpn, uint8_t msg_type) { int r = 0, optlen = 0, offset = 0; struct pico_ip4 broadcast = {0}, dns = {0}, destination = { .addr = 0xFFFFFFFF}; struct pico_dhcp_hdr *hdr = NULL; dns.addr = DHCP_SERVER_OPENDNS; broadcast.addr = dhcpn->dhcps->server_ip.addr | ~(dhcpn->dhcps->netmask.addr); optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_SERVERID + PICO_DHCP_OPTLEN_LEASETIME + PICO_DHCP_OPTLEN_NETMASK + PICO_DHCP_OPTLEN_ROUTER + PICO_DHCP_OPTLEN_BROADCAST + PICO_DHCP_OPTLEN_DNS + PICO_DHCP_OPTLEN_END; hdr = pico_zalloc(sizeof(struct pico_dhcp_hdr) + (uint32_t)optlen); hdr->op = PICO_DHCP_OP_REPLY; hdr->htype = PICO_DHCP_HTYPE_ETH; hdr->hlen = PICO_SIZE_ETH; hdr->xid = dhcpn->xid; hdr->yiaddr = dhcpn->ciaddr.addr; hdr->siaddr = dhcpn->dhcps->server_ip.addr; hdr->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE; memcpy(hdr->hwaddr, dhcpn->hwaddr.addr, PICO_SIZE_ETH); /* options */ offset += pico_dhcp_opt_msgtype(&hdr->options[offset], msg_type); offset += pico_dhcp_opt_serverid(&hdr->options[offset], &dhcpn->dhcps->server_ip); offset += pico_dhcp_opt_leasetime(&hdr->options[offset], dhcpn->dhcps->lease_time); offset += pico_dhcp_opt_netmask(&hdr->options[offset], &dhcpn->dhcps->netmask); offset += pico_dhcp_opt_router(&hdr->options[offset], &dhcpn->dhcps->server_ip); offset += pico_dhcp_opt_broadcast(&hdr->options[offset], &broadcast); offset += pico_dhcp_opt_dns(&hdr->options[offset], &dns); offset += pico_dhcp_opt_end(&hdr->options[offset]); destination.addr = hdr->yiaddr; r = pico_socket_sendto(dhcpn->dhcps->s, hdr, (int)(sizeof(struct pico_dhcp_hdr) + (uint32_t)optlen), &destination, PICO_DHCP_CLIENT_PORT); if (r < 0) dhcps_dbg("DHCP server WARNING: failure sending: %s!\n", strerror(pico_err)); return; } static void pico_dhcp_server_recv(struct pico_socket *s, uint8_t *buf, uint32_t len) { uint8_t msgtype = 0; int32_t optlen = (int32_t)(len - sizeof(struct pico_dhcp_hdr)); struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf; struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)hdr->options; struct pico_dhcp_server_negotiation *dhcpn = NULL; struct pico_ip4 reqip = {0}, server_id = {0}; struct pico_device *dev = NULL; if (!pico_dhcp_are_options_valid(hdr->options, optlen)) return; dev = pico_ipv4_link_find(&s->local_addr.ip4); dhcpn = pico_dhcp_server_find_negotiation(hdr->xid); if (!dhcpn) dhcpn = pico_dhcp_server_add_negotiation(dev, hdr); if (!ip_inrange(dhcpn->ciaddr.addr)) return; do { switch (opt->code) { case PICO_DHCP_OPT_PAD: break; case PICO_DHCP_OPT_END: break; case PICO_DHCP_OPT_MSGTYPE: msgtype = opt->ext.msg_type.type; dhcps_dbg("DHCP server: message type %u\n", msgtype); break; case PICO_DHCP_OPT_REQIP: reqip = opt->ext.req_ip.ip; dhcps_dbg("DHCP server: requested IP %08X\n", reqip.addr); break; case PICO_DHCP_OPT_SERVERID: server_id = opt->ext.server_id.ip; dhcps_dbg("DHCP server: server ID %08X\n", server_id.addr); break; default: dhcps_dbg("DHCP server WARNING: unsupported option %u\n", opt->code); break; } } while (pico_dhcp_next_option(&opt)); switch (msgtype) { case PICO_DHCP_MSG_DISCOVER: dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_OFFER); dhcpn->state = PICO_DHCP_STATE_OFFER; break; case PICO_DHCP_MSG_REQUEST: if ((dhcpn->state == PICO_DHCP_STATE_BOUND) && (!reqip.addr) && (!server_id.addr) && (hdr->ciaddr == dhcpn->ciaddr.addr)) dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_ACK); if (dhcpn->state == PICO_DHCP_STATE_OFFER) { dhcpn->state = PICO_DHCP_STATE_BOUND; dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_ACK); } break; default: dhcps_dbg("DHCP server WARNING: unsupported message type %u\n", msgtype); break; } return; }