Esempio n. 1
0
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;
}
Esempio n. 2
0
smcp_status_t
smcp_outbound_set_content_len(coap_size_t len) {
	smcp_get_current_instance()->outbound.content_len = len;
	return SMCP_STATUS_OK;
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
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;
}
Esempio n. 5
0
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)");
Esempio n. 6
0
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;
}
Esempio n. 7
0
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;
	}
Esempio n. 8
0
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;
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
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;
}
Esempio n. 11
0
smcp_session_type_t
smcp_plat_get_session_type(void)
{
	return smcp_get_current_instance()->plat.session_type;
}
Esempio n. 12
0
const smcp_sockaddr_t*
smcp_plat_get_local_sockaddr(void)
{
	return &smcp_get_current_instance()->plat.sockaddr_local;
}
Esempio n. 13
0
const smcp_sockaddr_t*
smcp_plat_get_remote_sockaddr(void)
{
	return &smcp_get_current_instance()->plat.sockaddr_remote;
}
Esempio n. 14
0
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;
}
Esempio n. 15
-1
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;
}