mirror of
https://github.com/obgm/libcoap.git
synced 2026-10-29 22:15:49 +03:00
249 lines
6.0 KiB
C
249 lines
6.0 KiB
C
/* coap -- simple implementation of the Constrained Application Protocol (CoAP)
|
|
* as defined in draft-ietf-core-coap-01
|
|
*
|
|
* (c) 2010 Olaf Bergmann <bergmann@tzi.org>
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <sys/select.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
|
|
#include "coap.h"
|
|
|
|
coap_pdu_t *
|
|
new_ack( coap_context_t *ctx, coap_queue_t *node ) {
|
|
coap_pdu_t *pdu = coap_new_pdu();
|
|
|
|
if (pdu) {
|
|
pdu->hdr->type = COAP_MESSAGE_ACK;
|
|
pdu->hdr->code = 0;
|
|
pdu->hdr->id = node->pdu->hdr->id;
|
|
}
|
|
|
|
return pdu;
|
|
}
|
|
|
|
coap_pdu_t *
|
|
new_response( coap_context_t *ctx, coap_queue_t *node, unsigned int code ) {
|
|
coap_pdu_t *pdu = new_ack(ctx, node);
|
|
|
|
if (pdu)
|
|
pdu->hdr->code = code;
|
|
|
|
return pdu;
|
|
}
|
|
|
|
void
|
|
add_contents( coap_pdu_t *pdu, unsigned int mediatype, unsigned int len, unsigned char *data ) {
|
|
unsigned char ct = COAP_MEDIATYPE_APPLICATION_LINK_FORMAT;
|
|
if (!pdu)
|
|
return;
|
|
|
|
/* add content-encoding */
|
|
coap_add_option(pdu, COAP_OPTION_CONTENT_TYPE, 1, &ct);
|
|
|
|
/* TODO: handle fragmentation (check result code) */
|
|
coap_add_data(pdu, len, data);
|
|
}
|
|
|
|
static unsigned char resources[] =
|
|
"</first-resource>;sh=/f;n=Example Resource 1,"
|
|
"</second-resource>;sh=/s;ct=41,42;n=second example";
|
|
|
|
void
|
|
message_handler( coap_context_t *ctx, coap_queue_t *node, void *data) {
|
|
coap_pdu_t *pdu = NULL;
|
|
|
|
#ifndef NDEBUG
|
|
printf("** process pdu: ");
|
|
coap_show_pdu( node->pdu );
|
|
#endif
|
|
|
|
if ( node->pdu->hdr->version != COAP_DEFAULT_VERSION ) {
|
|
debug("dropped packet with unknown version %u\n", node->pdu->hdr->version);
|
|
return;
|
|
}
|
|
|
|
switch (node->pdu->hdr->code) {
|
|
case COAP_REQUEST_GET :
|
|
pdu = new_response( ctx, node, COAP_RESPONSE_200 );
|
|
if (pdu) {
|
|
add_contents( pdu, COAP_MEDIATYPE_APPLICATION_LINK_FORMAT,
|
|
(unsigned int)strlen((char *)resources), resources );
|
|
}
|
|
break;
|
|
case COAP_REQUEST_POST:
|
|
case COAP_REQUEST_PUT:
|
|
case COAP_REQUEST_DELETE:
|
|
debug("request method not implemented: %u\n", node->pdu->hdr->code);
|
|
|
|
/* TODO: send 165 response */
|
|
pdu = new_response( ctx, node, COAP_RESPONSE_405 );
|
|
break;
|
|
default:
|
|
if ( node->pdu->hdr->code >= COAP_RESPONSE_100 && node->pdu->hdr->type == COAP_MESSAGE_CON ) {
|
|
pdu = new_ack( ctx, node );
|
|
}
|
|
}
|
|
|
|
if ( pdu && coap_send( ctx, &node->remote, pdu ) == COAP_INVALID_TID ) {
|
|
debug("message_handler: error sending reponse");
|
|
coap_delete_pdu(pdu);
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
usage( const char *program, const char *version) {
|
|
const char *p;
|
|
|
|
p = strrchr( program, '/' );
|
|
if ( p )
|
|
program = ++p;
|
|
|
|
fprintf( stderr, "%s v%s -- a small CoAP implementation\n"
|
|
"(c) 2010 Olaf Bergmann <bergmann@tzi.org>\n\n"
|
|
"usage: %s [-g group] [-p port] URI\n\n"
|
|
"\tURI can be an absolute or relative coap URI,\n"
|
|
"\t-g group\tjoin the given multicast group\n"
|
|
"\t-p port\t\tlisten on specified port\n",
|
|
program, version, program );
|
|
}
|
|
|
|
int
|
|
join( coap_context_t *ctx, char *group_name ){
|
|
struct ipv6_mreq mreq;
|
|
struct addrinfo *reslocal = NULL, *resmulti = NULL, hints, *ainfo;
|
|
int result = -1;
|
|
|
|
/* we have to resolve the link-local interface to get the interface id */
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_INET6;
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
result = getaddrinfo("::", NULL, &hints, &reslocal);
|
|
if ( result < 0 ) {
|
|
perror("join: cannot resolve link-local interface");
|
|
goto finish;
|
|
}
|
|
|
|
/* get the first suitable interface identifier */
|
|
for (ainfo = reslocal; ainfo != NULL; ainfo = ainfo->ai_next) {
|
|
if ( ainfo->ai_family == AF_INET6 ) {
|
|
mreq.ipv6mr_interface =
|
|
((struct sockaddr_in6 *)ainfo->ai_addr)->sin6_scope_id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_INET6;
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
/* resolve the multicast group address */
|
|
result = getaddrinfo(group_name, NULL, &hints, &resmulti);
|
|
|
|
if ( result < 0 ) {
|
|
perror("join: cannot resolve multicast address");
|
|
goto finish;
|
|
}
|
|
|
|
for (ainfo = resmulti; ainfo != NULL; ainfo = ainfo->ai_next) {
|
|
if ( ainfo->ai_family == AF_INET6 ) {
|
|
mreq.ipv6mr_multiaddr =
|
|
((struct sockaddr_in6 *)ainfo->ai_addr)->sin6_addr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
result = setsockopt( ctx->sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
|
|
(char *)&mreq, sizeof(mreq) );
|
|
if ( result < 0 )
|
|
perror("join: setsockopt");
|
|
|
|
finish:
|
|
freeaddrinfo(resmulti);
|
|
freeaddrinfo(reslocal);
|
|
|
|
return result;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv) {
|
|
coap_context_t *ctx;
|
|
fd_set readfds;
|
|
struct timeval tv, *timeout;
|
|
int result;
|
|
time_t now;
|
|
coap_queue_t *nextpdu;
|
|
unsigned short port = COAP_DEFAULT_PORT;
|
|
int opt;
|
|
char *group = NULL;
|
|
|
|
while ((opt = getopt(argc, argv, "g:p:")) != -1) {
|
|
switch (opt) {
|
|
case 'g' :
|
|
group = optarg;
|
|
break;
|
|
case 'p' :
|
|
port = atoi(optarg);
|
|
break;
|
|
default:
|
|
usage( argv[0], VERSION );
|
|
exit( 1 );
|
|
}
|
|
}
|
|
|
|
ctx = coap_new_context( port );
|
|
if ( !ctx )
|
|
return -1;
|
|
|
|
if ( group )
|
|
join( ctx, group );
|
|
|
|
coap_register_message_handler( ctx, message_handler );
|
|
|
|
while ( 1 ) {
|
|
FD_ZERO(&readfds);
|
|
FD_SET( ctx->sockfd, &readfds );
|
|
|
|
nextpdu = coap_peek_next( ctx );
|
|
|
|
time(&now);
|
|
while ( nextpdu && nextpdu->t <= now ) {
|
|
coap_retransmit( ctx, coap_pop_next( ctx ) );
|
|
nextpdu = coap_peek_next( ctx );
|
|
}
|
|
|
|
if ( nextpdu ) { /* set timeout if there is a pdu to send */
|
|
tv.tv_usec = 0;
|
|
tv.tv_sec = nextpdu->t - now;
|
|
timeout = &tv;
|
|
} else
|
|
timeout = NULL; /* no timeout otherwise */
|
|
|
|
result = select( ctx->sockfd + 1, &readfds, 0, 0, timeout );
|
|
|
|
if ( result < 0 ) { /* error */
|
|
perror("select");
|
|
} else if ( result > 0 ) { /* read from socket */
|
|
if ( FD_ISSET( ctx->sockfd, &readfds ) ) {
|
|
coap_read( ctx ); /* read received data */
|
|
coap_dispatch( ctx ); /* and dispatch PDUs from receivequeue */
|
|
}
|
|
}
|
|
}
|
|
|
|
coap_free_context( ctx );
|
|
|
|
return 0;
|
|
}
|