smcp_status_t smcp_outbound_set_uri( const char* uri, char flags ) { smcp_status_t ret = SMCP_STATUS_OK; smcp_t const self = smcp_get_current_instance(); SMCP_NON_RECURSIVE struct url_components_s components; SMCP_NON_RECURSIVE uint16_t toport; SMCP_NON_RECURSIVE char* uri_copy; memset((void*)&components, 0, sizeof(components)); toport = COAP_DEFAULT_PORT; smcp_plat_set_session_type(SMCP_SESSION_TYPE_UDP); uri_copy = NULL; require_action(uri, bail, ret = SMCP_STATUS_INVALID_ARGUMENT); { #if HAVE_ALLOCA uri_copy = alloca(strlen(uri) + 1); strcpy(uri_copy, uri); #elif SMCP_AVOID_MALLOC // Well, we can't use the stack and we can't // use malloc. Let's use what room we have left // in the packet buffer, since this is temporary anyway... // It helps a bunch that we know the user hasn't written // any content yet (because that would be an API violation) if (smcp_outbound_get_space_remaining() > strlen(uri) + 8) { uri_copy = self->outbound.content_ptr + self->outbound.content_len; // The options section may be expanding as we parse this, so // we should move ahead by a few bytes. We are helped out // by the fact that we will be writing the options in the // same order they appear in the URL. uri_copy += 8; strcpy(uri_copy, uri); } #else uri_copy = strdup(uri); #endif require_action(uri_copy != NULL, bail, ret = SMCP_STATUS_MALLOC_FAILURE); // Parse the URI. require_action_string( url_parse( uri_copy, &components ), bail, ret = SMCP_STATUS_URI_PARSE_FAILURE, "Unable to parse URL" ); if(!components.protocol && !components.host) { // Talking to ourself. components.protocol = "coap"; components.host = "::1"; toport = smcp_plat_get_port(smcp_get_current_instance()); flags |= SMCP_MSG_SKIP_AUTHORITY; } else if(components.port) { toport = (uint16_t)atoi(components.port); } DEBUG_PRINTF( "URI Parse: \"%s\" -> host=\"%s\" port=\"%u\" path=\"%s\"", uri, components.host, toport, components.path ); } if (components.protocol) { smcp_session_type_t session_type = smcp_session_type_from_uri_scheme(components.protocol); smcp_plat_set_session_type(session_type); if (NULL == components.port) { toport = smcp_default_port_from_session_type(session_type); } if (session_type == SMCP_SESSION_TYPE_NIL) { require_action_string( self->proxy_url, bail, ret=SMCP_STATUS_INVALID_ARGUMENT, "No proxy URL configured" ); require_action(uri != self->proxy_url,bail,ret = SMCP_STATUS_INVALID_ARGUMENT); ret = smcp_outbound_add_option(COAP_OPTION_PROXY_URI, uri, SMCP_CSTR_LEN); require_noerr(ret, bail); ret = smcp_outbound_set_uri(self->proxy_url,flags); goto bail; } } if (!(flags & SMCP_MSG_SKIP_AUTHORITY)) { if(components.host && !string_contains_colons(components.host)) { ret = smcp_outbound_add_option(COAP_OPTION_URI_HOST, components.host, SMCP_CSTR_LEN); require_noerr(ret, bail); } if(components.port) { ret = smcp_outbound_add_option_uint(COAP_OPTION_URI_PORT, toport); require_noerr(ret, bail); } } if ( !(flags & SMCP_MSG_SKIP_DESTADDR) && components.host && components.host[0]!=0 ) { ret = smcp_set_remote_sockaddr_from_host_and_port( components.host, toport ); require_noerr(ret, bail); } if (components.path) { SMCP_NON_RECURSIVE char* component; const bool has_trailing_slash = components.path[0]?('/' == components.path[strlen(components.path)-1]):false; // Move past any preceding slashes. while (components.path[0] == '/') { components.path++; } while (url_path_next_component(&components.path,&component)) { ret = smcp_outbound_add_option(COAP_OPTION_URI_PATH, component, SMCP_CSTR_LEN); require_noerr(ret,bail); } if (has_trailing_slash) { ret = smcp_outbound_add_option(COAP_OPTION_URI_PATH, NULL, 0); require_noerr(ret,bail); } } if (components.query) { SMCP_NON_RECURSIVE char* key; while (url_form_next_value(&components.query, &key, NULL)) { coap_size_t len = (coap_size_t)strlen(key); if (len) { ret = smcp_outbound_add_option(COAP_OPTION_URI_QUERY, key, len); } require_noerr(ret,bail); } } bail: if(ret) { DEBUG_PRINTF("URI Parse failed for URI: \"%s\"",uri); } #if !HAVE_ALLOCA && !SMCP_AVOID_MALLOC free(uri_copy); #endif return ret; }
smcp_status_t smcp_outbound_set_content_len(coap_size_t len) { smcp_get_current_instance()->outbound.content_len = len; return SMCP_STATUS_OK; }
smcp_status_t smcp_outbound_begin( smcp_t self, coap_code_t code, coap_transaction_type_t tt ) { smcp_status_t ret = SMCP_STATUS_FAILURE; SMCP_EMBEDDED_SELF_HOOK; check(!smcp_get_current_instance() || smcp_get_current_instance()==self); smcp_set_current_instance(self); #if SMCP_USE_CASCADE_COUNT require_action(self->cascade_count != 1, bail, ret = SMCP_STATUS_CASCADE_LOOP); #endif if (!self->is_processing_message) { smcp_plat_set_remote_sockaddr(NULL); smcp_plat_set_local_sockaddr(NULL); } self->outbound.max_packet_len = SMCP_MAX_PACKET_LENGTH; require_noerr((ret=smcp_plat_outbound_start(self,(uint8_t**)&self->outbound.packet,&self->outbound.max_packet_len)), bail); assert(NULL != self->outbound.packet); self->outbound.packet->tt = tt; self->outbound.packet->msg_id = self->outbound.next_tid; self->outbound.packet->code = code; self->outbound.packet->version = COAP_VERSION; // Set the token. if ( self->is_processing_message && self->inbound.packet != NULL && self->inbound.packet->token_len != 0 && code != COAP_CODE_EMPTY ) { self->outbound.packet->token_len = self->inbound.packet->token_len; memcpy(self->outbound.packet->token,self->inbound.packet->token,self->outbound.packet->token_len); } else if (code && (code < COAP_RESULT_100) && self->current_transaction) { // For sending a request. self->outbound.packet->token_len = sizeof(self->current_transaction->token); memcpy(self->outbound.packet->token,(void*)&self->current_transaction->token,self->outbound.packet->token_len); } else { self->outbound.packet->token_len = 0; } self->outbound.last_option_key = 0; self->outbound.content_ptr = (char*)self->outbound.packet->token + self->outbound.packet->token_len; *self->outbound.content_ptr++ = 0xFF; // start-of-content marker self->outbound.content_len = 0; self->force_current_outbound_code = false; self->is_responding = false; ret = SMCP_STATUS_OK; bail: return ret; }
smcp_status_t smcp_outbound_set_msg_id(coap_msg_id_t tid) { assert(smcp_get_current_instance()->outbound.packet); smcp_get_current_instance()->outbound.packet->msg_id = tid; return SMCP_STATUS_OK; }
smcp_t smcp_init( smcp_t self, uint16_t port ) { #if SMCP_EMBEDDED smcp_t self = smcp_get_current_instance(); #endif require(self != NULL, bail); if(port == 0) port = COAP_DEFAULT_PORT; smcp_sockaddr_t saddr = { #if SOCKADDR_HAS_LENGTH_FIELD .___smcp_len = sizeof(smcp_sockaddr_t), #endif .___smcp_family = SMCP_BSD_SOCKETS_NET_FAMILY, .smcp_port = htons(port), }; // Clear the entire structure. memset(self, 0, sizeof(*self)); // Set up the UDP port for listening. uint16_t attempts = 0x7FFF; self->mcfd = -1; self->fd = -1; errno = 0; self->fd = socket(SMCP_BSD_SOCKETS_NET_FAMILY, SOCK_DGRAM, IPPROTO_UDP); int prev_errno = errno; require_action_string( self->fd >= 0, bail, ( smcp_release(self), self = NULL ), strerror(prev_errno) ); #if defined(IPV6_V6ONLY) && SMCP_BSD_SOCKETS_NET_FAMILY==AF_INET6 { int value = 0; /* explicitly allow ipv4 traffic too (required on bsd and some debian installations) */ if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value)) < 0) { DEBUG_PRINTF(CSTR("Socket won't allow IPv4 connections")); } } #endif // Keep attempting to bind until we find a port that works. while(bind(self->fd, (struct sockaddr*)&saddr, sizeof(saddr)) != 0) { // We should only continue trying if errno == EADDRINUSE. require_action_string(errno == EADDRINUSE, bail, { DEBUG_PRINTF(CSTR("errno=%d"), errno); smcp_release( self); self = NULL; }, "Failed to bind socket"); port++; // Make sure we aren't in an infinite loop. require_action_string(--attempts, bail, { DEBUG_PRINTF(CSTR("errno=%d"), errno); smcp_release( self); self = NULL; }, "Failed to bind socket (ran out of ports)");
smcp_status_t smcp_node_route(smcp_node_t node, smcp_request_handler_func* func, void** context) { smcp_status_t ret = 0; smcp_t const self = smcp_get_current_instance(); smcp_inbound_reset_next_option(); { // TODO: Rewrite this to be more efficient. const uint8_t* prev_option_ptr = self->inbound.this_option; coap_option_key_t prev_key = 0; coap_option_key_t key; const uint8_t* value; coap_size_t value_len; while ((key = smcp_inbound_next_option(&value, &value_len)) != COAP_OPTION_INVALID) { if (key > COAP_OPTION_URI_PATH) { self->inbound.this_option = prev_option_ptr; self->inbound.last_option_key = prev_key; break; } else if (key == COAP_OPTION_URI_PATH) { smcp_node_t next = smcp_node_find( node, (const char*)value, (int)value_len ); if (next) { node = next; } else { self->inbound.this_option = prev_option_ptr; self->inbound.last_option_key = prev_key; break; } } else if(key==COAP_OPTION_URI_HOST) { // Skip host at the moment, // because we don't do virtual hosting yet. } else if(key==COAP_OPTION_URI_PORT) { // Skip port at the moment, // because we don't do virtual hosting yet. } else if(key==COAP_OPTION_PROXY_URI) { // Skip the proxy URI for now. } else if(key==COAP_OPTION_CONTENT_TYPE) { // Skip. } else { if(COAP_OPTION_IS_CRITICAL(key)) { ret=SMCP_STATUS_BAD_OPTION; assert_printf("Unrecognized option %d, \"%s\"", key, coap_option_key_to_cstr(key, false) ); goto bail; } } prev_option_ptr = self->inbound.this_option; prev_key = self->inbound.last_option_key; } } *func = (void*)node->request_handler; if(node->context) { *context = node->context; } else { *context = (void*)node; } bail: return ret; }
smcp_status_t cgi_node_request_handler( cgi_node_t node ) { smcp_status_t ret = 0; cgi_node_request_t request = NULL; uint32_t block2_option = BLOCK_OPTION_DEFAULT; uint32_t block1_option = BLOCK_OPTION_UNSPECIFIED; smcp_method_t method = smcp_inbound_get_code(); require(node,bail); node->interface = smcp_get_current_instance(); if (method==COAP_METHOD_GET) { ret = 0; } require_noerr(ret,bail); { const uint8_t* value; coap_size_t value_len; coap_option_key_t key; while ((key=smcp_inbound_next_option(&value, &value_len))!=COAP_OPTION_INVALID) { if (key == COAP_OPTION_BLOCK2) { uint8_t i; block2_option = 0; for (i = 0; i < value_len; i++) { block2_option = (block2_option << 8) + value[i]; } } if (key == COAP_OPTION_BLOCK1) { uint8_t i; block1_option = 0; for (i = 0; i < value_len; i++) { block1_option = (block1_option << 8) + value[i]; } } } } // Make sure this is a supported method. switch (method) { case COAP_METHOD_GET: case COAP_METHOD_PUT: case COAP_METHOD_POST: case COAP_METHOD_DELETE: break; default: ret = SMCP_STATUS_NOT_IMPLEMENTED; goto bail; break; } request = cgi_node_get_associated_request(node); if (request == NULL) { // Possibly new request. // Assume new for now, but we may need to do additional checks. // We don't support non-zero block indexes on the first packet. require_action((block2_option>>4) == 0, bail, ret = SMCP_STATUS_INVALID_ARGUMENT); request = cgi_node_create_request(node); require_action(request != NULL, bail, ret = SMCP_STATUS_FAILURE); request->block2 = block2_option; }
smcp_status_t smcp_curl_proxy_request_handler( smcp_curl_proxy_node_t node ) { smcp_status_t ret = SMCP_STATUS_NOT_ALLOWED; smcp_curl_request_t request = NULL; struct curl_slist *headerlist=NULL; smcp_method_t method = smcp_inbound_get_code(); //require_action(method<=COAP_METHOD_DELETE,bail,ret = SMCP_STATUS_NOT_ALLOWED); //require_action(COAP_OPTION_URI_PATH!=smcp_inbound_peek_option(NULL,NULL),bail,ret=SMCP_STATUS_NOT_FOUND); node->interface = smcp_get_current_instance(); smcp_inbound_reset_next_option(); request = smcp_curl_request_create(); request->proxy_node = node; require_action(request!=NULL,bail,ret = SMCP_STATUS_MALLOC_FAILURE); switch(method) { case COAP_METHOD_GET: curl_easy_setopt(request->curl, CURLOPT_CUSTOMREQUEST, "GET"); break; case COAP_METHOD_PUT: curl_easy_setopt(request->curl, CURLOPT_PUT, 1L); break; case COAP_METHOD_POST: curl_easy_setopt(request->curl, CURLOPT_POST, 1L); break; case COAP_METHOD_DELETE: curl_easy_setopt(request->curl, CURLOPT_CUSTOMREQUEST, "DELETE"); break; default: ret = SMCP_STATUS_NOT_ALLOWED; break; } { coap_option_key_t key; const uint8_t* value; coap_size_t value_len; while((key=smcp_inbound_next_option(&value, &value_len))!=COAP_OPTION_INVALID) { if(key==COAP_OPTION_PROXY_URI) { char uri[value_len+1]; memcpy(uri,value,value_len); uri[value_len] = 0; curl_easy_setopt(request->curl, CURLOPT_URL, uri); assert_printf("CuRL URL: \"%s\"",uri); ret = 0; } else if(key==COAP_OPTION_URI_HOST) { } else if(key==COAP_OPTION_URI_PORT) { } else if(key==COAP_OPTION_URI_PATH) { } else if(key==COAP_OPTION_URI_QUERY) { } else if(key==COAP_OPTION_CONTENT_TYPE || key==COAP_OPTION_ACCEPT) { const char* option_name = coap_option_key_to_cstr(key, false); const char* value_string = coap_content_type_to_cstr(value[1]); char header[strlen(option_name)+strlen(value_string)+3]; strcpy(header,option_name); strcat(header,": "); strcat(header,value_string); headerlist = curl_slist_append(headerlist, header); assert_printf("CuRL HEADER: \"%s\"",header); } else { if(coap_option_value_is_string(key)) { const char* option_name = coap_option_key_to_cstr(key, false); char header[strlen(option_name)+value_len+3]; strcpy(header,option_name); strcat(header,": "); strncat(header,(const char*)value,value_len); assert_printf("CuRL HEADER: \"%s\"",header); headerlist = curl_slist_append(headerlist, header); } } } } require_noerr(ret,bail); if(smcp_inbound_get_content_len()) { coap_size_t len = smcp_inbound_get_content_len(); request->output_content = calloc(1,len+1); request->output_content_len = len; memcpy(request->output_content,smcp_inbound_get_content_ptr(),len); curl_easy_setopt(request->curl, CURLOPT_READFUNCTION, ReadMemoryCallback); curl_easy_setopt(request->curl, CURLOPT_READDATA, (void *)request); } curl_easy_setopt(request->curl, CURLOPT_USERAGENT, "smcp-curl-proxy/1.0"); curl_easy_setopt(request->curl, CURLOPT_HTTPHEADER, headerlist),headerlist=NULL; curl_easy_setopt(request->curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(request->curl, CURLOPT_WRITEDATA, (void *)request); ret = smcp_start_async_response(&request->async_response,0); require_noerr(ret,bail); if(node->curl_multi_handle) curl_multi_add_handle(node->curl_multi_handle, request->curl); else curl_easy_perform(request->curl); bail: if(headerlist) curl_slist_free_all(headerlist); if(ret && request) smcp_curl_request_release(request); return ret; }
smcp_status_t smcp_plat_outbound_finish(smcp_t self,const uint8_t* data_ptr, coap_size_t data_len, int flags) { SMCP_EMBEDDED_SELF_HOOK; smcp_status_t ret = SMCP_STATUS_FAILURE; ssize_t sent_bytes = -1; int fd; #if SMCP_DTLS if (smcp_plat_get_session_type() == SMCP_SESSION_TYPE_DTLS) { ret = smcp_plat_ssl_outbound_packet_process(self, data_ptr, data_len); } else #endif if (smcp_plat_get_session_type() == SMCP_SESSION_TYPE_UDP) { fd = smcp_get_current_instance()->plat.fd_udp; assert(fd >= 0); require(data_len > 0, bail); #if VERBOSE_DEBUG { char addr_str[50] = "???"; uint16_t port = ntohs(smcp_plat_get_remote_sockaddr()->smcp_port); SMCP_ADDR_NTOP(addr_str,sizeof(addr_str),&smcp_plat_get_remote_sockaddr()->smcp_addr); DEBUG_PRINTF("smcp(%p): Outbound packet to [%s]:%d", self,addr_str,(int)port); coap_dump_header( SMCP_DEBUG_OUT_FILE, "Outbound:\t", (struct coap_header_s*)data_ptr, (coap_size_t)data_len ); } #endif sent_bytes = sendtofrom( fd, data_ptr, data_len, 0, (struct sockaddr *)smcp_plat_get_remote_sockaddr(), sizeof(smcp_sockaddr_t), (struct sockaddr *)smcp_plat_get_local_sockaddr(), sizeof(smcp_sockaddr_t) ); require_action_string( (sent_bytes >= 0), bail, ret = SMCP_STATUS_ERRNO, strerror(errno) ); require_action_string( (sent_bytes == data_len), bail, ret = SMCP_STATUS_FAILURE, "sendto() returned less than len" ); ret = SMCP_STATUS_OK; } else { ret = SMCP_STATUS_NOT_IMPLEMENTED; } bail: return ret; }
smcp_status_t cgi_node_request_change_state(cgi_node_t node, cgi_node_request_t request, cgi_node_state_t new_state) { // TODO: Possibly do more later...? syslog(LOG_INFO, "cgi-node: %d -> %d", request->state, new_state); if(request->state == new_state) { // Same state, do nothing. } else if ( request->state == CGI_NODE_STATE_INACTIVE && new_state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_REQ ) { } else if ( request->state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_REQ && new_state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_FD ) { smcp_start_async_response(&request->async_response, 0); } else if ( request->state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_FD && new_state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_ACK ) { cgi_node_send_next_block(node,request); } else if ( request->state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_ACK && new_state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_REQ ) { if (request->transaction) { smcp_transaction_end(smcp_get_current_instance(),request->transaction); request->transaction = NULL; } } else if ( ( (request->state == CGI_NODE_STATE_ACTIVE_BLOCK2_WAIT_REQ) || (request->state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_REQ) ) && ( new_state == CGI_NODE_STATE_ACTIVE_BLOCK2_WAIT_FD) ) { if(!request->stdin_buffer_len && request->fd_cmd_stdin>=0) { close(request->fd_cmd_stdin); request->fd_cmd_stdin = -1; } smcp_start_async_response(&request->async_response, 0); } else if(request->state == CGI_NODE_STATE_ACTIVE_BLOCK2_WAIT_FD && new_state == CGI_NODE_STATE_ACTIVE_BLOCK2_WAIT_ACK ) { if(!request->stdin_buffer_len && request->fd_cmd_stdin>=0) { close(request->fd_cmd_stdin); request->fd_cmd_stdin = -1; } cgi_node_send_next_block(node,request); } else if(request->state == CGI_NODE_STATE_ACTIVE_BLOCK2_WAIT_ACK && new_state == CGI_NODE_STATE_ACTIVE_BLOCK2_WAIT_REQ ) { if(!request->stdin_buffer_len && request->fd_cmd_stdin>=0) { close(request->fd_cmd_stdin); request->fd_cmd_stdin = -1; } //cgi_node_request_pop_bytes_from_stdout(request,(1<<((request->block2&0x7)+4))); if(request->transaction) { smcp_transaction_end(smcp_get_current_instance(),request->transaction); request->transaction = NULL; } } else if(new_state == CGI_NODE_STATE_FINISHED) { if(request->transaction) { smcp_transaction_end(smcp_get_current_instance(),request->transaction); request->transaction = NULL; } close(request->fd_cmd_stdin); request->fd_cmd_stdin = -1; close(request->fd_cmd_stdout); request->fd_cmd_stdout = -1; if(request->pid != 0 && request->pid != -1) { int status; kill(request->pid,SIGTERM); if(waitpid(request->pid, &status, WNOHANG) == request->pid) { request->pid = 0; } } } else { // INVALID STATE TRANSITION! syslog(LOG_ERR, "cgi-node: BAD STATE CHANGE: %d -> %d", request->state, new_state); abort(); } request->state = new_state; return SMCP_STATUS_OK; }
smcp_session_type_t smcp_plat_get_session_type(void) { return smcp_get_current_instance()->plat.session_type; }
const smcp_sockaddr_t* smcp_plat_get_local_sockaddr(void) { return &smcp_get_current_instance()->plat.sockaddr_local; }
const smcp_sockaddr_t* smcp_plat_get_remote_sockaddr(void) { return &smcp_get_current_instance()->plat.sockaddr_remote; }
smcp_status_t smcp_plat_outbound_finish(smcp_t self,const uint8_t* data_ptr, coap_size_t data_len, int flags) { SMCP_EMBEDDED_SELF_HOOK; smcp_status_t ret = SMCP_STATUS_FAILURE; assert(uip_udp_conn == self->plat.udp_conn); uip_slen = data_len; require_action(uip_slen<SMCP_MAX_PACKET_LENGTH, bail, ret = SMCP_STATUS_MESSAGE_TOO_BIG); if (data_ptr != uip_sappdata) { memmove( uip_sappdata, data_ptr, uip_slen ); data_ptr = (const uint8_t*)uip_sappdata; } #if 0 // TODO: For some reason this isn't working anymore. Investigate. if(self->is_responding) { // We are responding, let uIP handle preparing the packet. } else #endif { // Here we explicitly tickle UIP to send the packet. // Change the remote IP address temporarily. uip_ipaddr_copy(&uip_udp_conn->ripaddr, &self->plat.sockaddr_remote.smcp_addr); smcp_get_current_instance()->plat.udp_conn->rport = self->plat.sockaddr_remote.smcp_port; uip_process(UIP_UDP_SEND_CONN); #if UIP_CONF_IPV6_MULTICAST /* Let the multicast engine process the datagram before we send it */ if (uip_is_addr_mcast_routable(&uip_udp_conn->ripaddr)) { UIP_MCAST6.out(); } #endif /* UIP_IPV6_MULTICAST */ // TODO: This next part is somewhat contiki-ish. Abstract somehow? #if UIP_CONF_IPV6 tcpip_ipv6_output(); #else tcpip_output(); #endif // Since we just sent out packet, we need to zero out uip_slen // to prevent uIP from trying to send out a packet. uip_slen = 0; // Make our remote address unspecified again, so that we can continue // to receive traffic. memset(&smcp_get_current_instance()->plat.udp_conn->ripaddr, 0, sizeof(uip_ipaddr_t)); smcp_get_current_instance()->plat.udp_conn->rport = 0; } ret = SMCP_STATUS_OK; bail: return ret; }
smcp_status_t smcp_plat_process( smcp_t self ) { SMCP_EMBEDDED_SELF_HOOK; smcp_status_t ret = 0; int tmp; struct pollfd polls[4]; int poll_count; poll_count = smcp_plat_update_pollfds(self, polls, sizeof(polls)/sizeof(polls[0])); if (poll_count > (int)(sizeof(polls)/sizeof(*polls))) { poll_count = sizeof(polls)/sizeof(*polls); } errno = 0; tmp = poll(polls, poll_count, 0); // Ensure that poll did not fail with an error. require_action_string( errno == 0, bail, ret = SMCP_STATUS_ERRNO, strerror(errno) ); if(tmp > 0) { for (tmp = 0; tmp < poll_count; tmp++) { if (!polls[tmp].revents) { continue; } else { char packet[SMCP_MAX_PACKET_LENGTH+1]; smcp_sockaddr_t remote_saddr = {}; smcp_sockaddr_t local_saddr = {}; ssize_t packet_len = 0; char cmbuf[0x100]; struct iovec iov = { packet, SMCP_MAX_PACKET_LENGTH }; struct msghdr msg = { .msg_name = &remote_saddr, .msg_namelen = sizeof(remote_saddr), .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmbuf, .msg_controllen = sizeof(cmbuf), }; struct cmsghdr *cmsg; packet_len = recvmsg(polls[tmp].fd, &msg, 0); require_action(packet_len > 0, bail, ret = SMCP_STATUS_ERRNO); packet[packet_len] = 0; for ( cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg) ) { if (cmsg->cmsg_level != SMCP_IPPROTO || cmsg->cmsg_type != SMCP_PKTINFO ) { continue; } // Preinitialize some of the fields. local_saddr = remote_saddr; #if SMCP_BSD_SOCKETS_NET_FAMILY==AF_INET6 struct in6_pktinfo *pi = (struct in6_pktinfo *)CMSG_DATA(cmsg); local_saddr.smcp_addr = pi->ipi6_addr; local_saddr.sin6_scope_id = pi->ipi6_ifindex; #elif SMCP_BSD_SOCKETS_NET_FAMILY==AF_INET struct in_pktinfo *pi = (struct in_pktinfo *)CMSG_DATA(cmsg); local_saddr.smcp_addr = pi->ipi_addr; #endif local_saddr.smcp_port = htons(get_port_for_fd(polls[tmp].fd)); self->plat.pktinfo = *pi; } smcp_set_current_instance(self); smcp_plat_set_remote_sockaddr(&remote_saddr); smcp_plat_set_local_sockaddr(&local_saddr); if (self->plat.fd_udp == polls[tmp].fd) { smcp_plat_set_session_type(SMCP_SESSION_TYPE_UDP); ret = smcp_inbound_packet_process(self, packet, (coap_size_t)packet_len, 0); require_noerr(ret, bail); #if SMCP_DTLS } else if (self->plat.fd_dtls == polls[tmp].fd) { smcp_plat_set_session_type(SMCP_SESSION_TYPE_DTLS); smcp_plat_ssl_inbound_packet_process( self, packet, (coap_size_t)packet_len ); #endif } } } } smcp_handle_timers(self); bail: smcp_set_current_instance(NULL); self->is_responding = false; return ret; } smcp_status_t smcp_plat_lookup_hostname(const char* hostname, smcp_sockaddr_t* saddr, int flags) { smcp_status_t ret; struct addrinfo hint = { .ai_flags = AI_ADDRCONFIG, .ai_family = AF_UNSPEC, }; struct addrinfo *results = NULL; struct addrinfo *iter = NULL; #if SMCP_BSD_SOCKETS_NET_FAMILY != AF_INET6 hint.ai_family = SMCP_BSD_SOCKETS_NET_FAMILY; #endif if ((flags & (SMCP_LOOKUP_HOSTNAME_FLAG_IPV4_ONLY|SMCP_LOOKUP_HOSTNAME_FLAG_IPV6_ONLY)) == (SMCP_LOOKUP_HOSTNAME_FLAG_IPV4_ONLY|SMCP_LOOKUP_HOSTNAME_FLAG_IPV6_ONLY)) { ret = SMCP_STATUS_INVALID_ARGUMENT; goto bail; } else if ((flags & SMCP_LOOKUP_HOSTNAME_FLAG_IPV4_ONLY) == SMCP_LOOKUP_HOSTNAME_FLAG_IPV4_ONLY) { hint.ai_family = AF_INET; } else if ((flags & SMCP_LOOKUP_HOSTNAME_FLAG_IPV6_ONLY) == SMCP_LOOKUP_HOSTNAME_FLAG_IPV6_ONLY) { hint.ai_family = AF_INET6; } memset(saddr, 0, sizeof(*saddr)); saddr->___smcp_family = SMCP_BSD_SOCKETS_NET_FAMILY; #if SOCKADDR_HAS_LENGTH_FIELD saddr->___smcp_len = sizeof(*saddr); #endif int error = getaddrinfo(hostname, NULL, &hint, &results); #if SMCP_BSD_SOCKETS_NET_FAMILY==AF_INET6 if(error && (inet_addr(hostname) != INADDR_NONE)) { char addr_v4mapped_str[8 + strlen(hostname)]; hint.ai_family = AF_INET6; hint.ai_flags = AI_ALL | AI_V4MAPPED, strcpy(addr_v4mapped_str,"::ffff:"); strcat(addr_v4mapped_str,hostname); error = getaddrinfo(addr_v4mapped_str, NULL, &hint, &results ); } #endif if (EAI_AGAIN == error) { ret = SMCP_STATUS_WAIT_FOR_DNS; goto bail; } #ifdef TM_EWOULDBLOCK if (TM_EWOULDBLOCK == error) { ret = SMCP_STATUS_WAIT_FOR_DNS; goto bail; } #endif require_action_string( !error, bail, ret = SMCP_STATUS_HOST_LOOKUP_FAILURE, gai_strerror(error) ); // Move to the first recognized result for(iter = results;iter && (iter->ai_family!=AF_INET6 && iter->ai_family!=AF_INET);iter=iter->ai_next); require_action( iter, bail, ret = SMCP_STATUS_HOST_LOOKUP_FAILURE ); #if SMCP_BSD_SOCKETS_NET_FAMILY==AF_INET6 if(iter->ai_family == AF_INET) { struct sockaddr_in *v4addr = (void*)iter->ai_addr; saddr->sin6_addr.s6_addr[10] = 0xFF; saddr->sin6_addr.s6_addr[11] = 0xFF; memcpy(&saddr->sin6_addr.s6_addr[12], &v4addr->sin_addr.s_addr, 4); } else #endif if(iter->ai_family == SMCP_BSD_SOCKETS_NET_FAMILY) { memcpy(saddr, iter->ai_addr, iter->ai_addrlen); } if(SMCP_IS_ADDR_MULTICAST(&saddr->smcp_addr)) { smcp_t const self = smcp_get_current_instance(); check(self->outbound.packet->tt != COAP_TRANS_TYPE_CONFIRMABLE); if(self->outbound.packet->tt == COAP_TRANS_TYPE_CONFIRMABLE) { self->outbound.packet->tt = COAP_TRANS_TYPE_NONCONFIRMABLE; } } ret = SMCP_STATUS_OK; bail: if(results) freeaddrinfo(results); return ret; }