int main(int argc, char *argv[]) { client_data_t data; int result; lwm2m_context_t * lwm2mH = NULL; int i; const char * localPort = "56830"; const char * server = NULL; const char * serverPort = LWM2M_STANDARD_PORT_STR; char * name = "testlwm2mclient"; int lifetime = 300; int batterylevelchanging = 0; time_t reboot_time = 0; int opt; bool bootstrapRequested = false; bool serverPortChanged = false; #ifdef LWM2M_BOOTSTRAP lwm2m_client_state_t previousState = STATE_INITIAL; #endif char * pskId = NULL; char * psk = NULL; uint16_t pskLen = -1; char * pskBuffer = NULL; /* * The function start by setting up the command line interface (which may or not be useful depending on your project) * * This is an array of commands describes as { name, description, long description, callback, userdata }. * The firsts tree are easy to understand, the callback is the function that will be called when this command is typed * and in the last one will be stored the lwm2m context (allowing access to the server settings and the objects). */ command_desc_t commands[] = { {"list", "List known servers.", NULL, prv_output_servers, NULL}, {"change", "Change the value of resource.", " change URI [DATA]\r\n" " URI: uri of the resource such as /3/0, /3/0/2\r\n" " DATA: (optional) new value\r\n", prv_change, NULL}, {"update", "Trigger a registration update", " update SERVER\r\n" " SERVER: short server id such as 123\r\n", prv_update, NULL}, #ifdef LWM2M_BOOTSTRAP {"bootstrap", "Initiate a DI bootstrap process", NULL, prv_initiate_bootstrap, NULL}, {"dispb", "Display current backup of objects/instances/resources\r\n" "\t(only security and server objects are backupped)", NULL, prv_display_backup, NULL}, #endif {"ls", "List Objects and Instances", NULL, prv_object_list, NULL}, {"disp", "Display current objects/instances/resources", NULL, prv_display_objects, NULL}, {"dump", "Dump an Object", "dump URI" "URI: uri of the Object or Instance such as /3/0, /1\r\n", prv_object_dump, NULL}, {"add", "Add support of object 1024", NULL, prv_add, NULL}, {"rm", "Remove support of object 1024", NULL, prv_remove, NULL}, {"quit", "Quit the client gracefully.", NULL, prv_quit, NULL}, {"^C", "Quit the client abruptly (without sending a de-register message).", NULL, NULL, NULL}, COMMAND_END_LIST }; memset(&data, 0, sizeof(client_data_t)); data.addressFamily = AF_INET6; opt = 1; while (opt < argc) { if (argv[opt] == NULL || argv[opt][0] != '-' || argv[opt][2] != 0) { print_usage(); return 0; } switch (argv[opt][1]) { case 'b': bootstrapRequested = true; if (!serverPortChanged) serverPort = LWM2M_BSSERVER_PORT_STR; break; case 'c': batterylevelchanging = 1; break; case 't': opt++; if (opt >= argc) { print_usage(); return 0; } if (1 != sscanf(argv[opt], "%d", &lifetime)) { print_usage(); return 0; } break; #ifdef WITH_TINYDTLS case 'i': opt++; if (opt >= argc) { print_usage(); return 0; } pskId = argv[opt]; break; case 's': opt++; if (opt >= argc) { print_usage(); return 0; } psk = argv[opt]; break; #endif case 'n': opt++; if (opt >= argc) { print_usage(); return 0; } name = argv[opt]; break; case 'l': opt++; if (opt >= argc) { print_usage(); return 0; } localPort = argv[opt]; break; case 'h': opt++; if (opt >= argc) { print_usage(); return 0; } server = argv[opt]; break; case 'p': opt++; if (opt >= argc) { print_usage(); return 0; } serverPort = argv[opt]; serverPortChanged = true; break; case '4': data.addressFamily = AF_INET; break; default: print_usage(); return 0; } opt += 1; } if (!server) { server = (AF_INET == data.addressFamily ? DEFAULT_SERVER_IPV4 : DEFAULT_SERVER_IPV6); } /* *This call an internal function that create an IPV6 socket on the port 5683. */ fprintf(stderr, "Trying to bind LWM2M Client to port %s\r\n", localPort); data.sock = create_socket(localPort, data.addressFamily); if (data.sock < 0) { fprintf(stderr, "Failed to open socket: %d %s\r\n", errno, strerror(errno)); return -1; } /* * Now the main function fill an array with each object, this list will be later passed to liblwm2m. * Those functions are located in their respective object file. */ #ifdef WITH_TINYDTLS if (psk != NULL) { pskLen = strlen(psk) / 2; pskBuffer = malloc(pskLen); if (NULL == pskBuffer) { fprintf(stderr, "Failed to create PSK binary buffer\r\n"); return -1; } // Hex string to binary char *h = psk; char *b = pskBuffer; char xlate[] = "0123456789ABCDEF"; for ( ; *h; h += 2, ++b) { char *l = strchr(xlate, toupper(*h)); char *r = strchr(xlate, toupper(*(h+1))); if (!r || !l) { fprintf(stderr, "Failed to parse Pre-Shared-Key HEXSTRING\r\n"); return -1; } *b = ((l - xlate) << 4) + (r - xlate); } } #endif char serverUri[50]; int serverId = 123; sprintf (serverUri, "coap://%s:%s", server, serverPort); #ifdef LWM2M_BOOTSTRAP objArray[0] = get_security_object(serverId, serverUri, pskId, pskBuffer, pskLen, bootstrapRequested); #else objArray[0] = get_security_object(serverId, serverUri, pskId, pskBuffer, pskLen, false); #endif if (NULL == objArray[0]) { fprintf(stderr, "Failed to create security object\r\n"); return -1; } data.securityObjP = objArray[0]; objArray[1] = get_server_object(serverId, "U", lifetime, false); if (NULL == objArray[1]) { fprintf(stderr, "Failed to create server object\r\n"); return -1; } objArray[2] = get_object_device(); if (NULL == objArray[2]) { fprintf(stderr, "Failed to create Device object\r\n"); return -1; } objArray[3] = get_object_firmware(); if (NULL == objArray[3]) { fprintf(stderr, "Failed to create Firmware object\r\n"); return -1; } objArray[4] = get_object_location(); if (NULL == objArray[4]) { fprintf(stderr, "Failed to create location object\r\n"); return -1; } objArray[5] = get_test_object(); if (NULL == objArray[5]) { fprintf(stderr, "Failed to create test object\r\n"); return -1; } objArray[6] = get_object_conn_m(); if (NULL == objArray[6]) { fprintf(stderr, "Failed to create connectivity monitoring object\r\n"); return -1; } objArray[7] = get_object_conn_s(); if (NULL == objArray[7]) { fprintf(stderr, "Failed to create connectivity statistics object\r\n"); return -1; } int instId = 0; objArray[8] = acc_ctrl_create_object(); if (NULL == objArray[8]) { fprintf(stderr, "Failed to create Access Control object\r\n"); return -1; } else if (acc_ctrl_obj_add_inst(objArray[8], instId, 3, 0, serverId)==false) { fprintf(stderr, "Failed to create Access Control object instance\r\n"); return -1; } else if (acc_ctrl_oi_add_ac_val(objArray[8], instId, 0, 0b000000000001111)==false) { fprintf(stderr, "Failed to create Access Control ACL default resource\r\n"); return -1; } else if (acc_ctrl_oi_add_ac_val(objArray[8], instId, 999, 0b000000000000001)==false) { fprintf(stderr, "Failed to create Access Control ACL resource for serverId: 999\r\n"); return -1; } /* * The liblwm2m library is now initialized with the functions that will be in * charge of communication */ lwm2mH = lwm2m_init(&data); if (NULL == lwm2mH) { fprintf(stderr, "lwm2m_init() failed\r\n"); return -1; } #ifdef WITH_TINYDTLS data.lwm2mH = lwm2mH; #endif /* * We configure the liblwm2m library with the name of the client - which shall be unique for each client - * the number of objects we will be passing through and the objects array */ result = lwm2m_configure(lwm2mH, name, NULL, NULL, OBJ_COUNT, objArray); if (result != 0) { fprintf(stderr, "lwm2m_configure() failed: 0x%X\r\n", result); return -1; } signal(SIGINT, handle_sigint); /** * Initialize value changed callback. */ init_value_change(lwm2mH); /* * As you now have your lwm2m context complete you can pass it as an argument to all the command line functions * precedently viewed (first point) */ for (i = 0 ; commands[i].name != NULL ; i++) { commands[i].userData = (void *)lwm2mH; } fprintf(stdout, "LWM2M Client \"%s\" started on port %s\r\n", name, localPort); fprintf(stdout, "> "); fflush(stdout); /* * We now enter in a while loop that will handle the communications from the server */ while (0 == g_quit) { struct timeval tv; fd_set readfds; if (g_reboot) { time_t tv_sec; tv_sec = lwm2m_gettime(); if (0 == reboot_time) { reboot_time = tv_sec + 5; } if (reboot_time < tv_sec) { /* * Message should normally be lost with reboot ... */ fprintf(stderr, "reboot time expired, rebooting ..."); system_reboot(); } else { tv.tv_sec = reboot_time - tv_sec; } } else if (batterylevelchanging) { update_battery_level(lwm2mH); tv.tv_sec = 5; } else { tv.tv_sec = 60; } tv.tv_usec = 0; FD_ZERO(&readfds); FD_SET(data.sock, &readfds); FD_SET(STDIN_FILENO, &readfds); /* * This function does two things: * - first it does the work needed by liblwm2m (eg. (re)sending some packets). * - Secondly it adjusts the timeout value (default 60s) depending on the state of the transaction * (eg. retransmission) and the time between the next operation */ result = lwm2m_step(lwm2mH, &(tv.tv_sec)); if (result != 0) { fprintf(stderr, "lwm2m_step() failed: 0x%X\r\n", result); if(previousState == STATE_BOOTSTRAPPING) { #ifdef WITH_LOGS fprintf(stdout, "[BOOTSTRAP] restore security and server objects\r\n"); #endif prv_connections_free(lwm2mH); prv_restore_objects(lwm2mH); lwm2mH->state = STATE_INITIAL; } else return -1; } #ifdef LWM2M_BOOTSTRAP update_bootstrap_info(&previousState, lwm2mH); #endif /* * This part will set up an interruption until an event happen on SDTIN or the socket until "tv" timed out (set * with the precedent function) */ result = select(FD_SETSIZE, &readfds, NULL, NULL, &tv); if (result < 0) { if (errno != EINTR) { fprintf(stderr, "Error in select(): %d %s\r\n", errno, strerror(errno)); } } else if (result > 0) { uint8_t buffer[MAX_PACKET_SIZE]; int numBytes; /* * If an event happens on the socket */ if (FD_ISSET(data.sock, &readfds)) { struct sockaddr_storage addr; socklen_t addrLen; addrLen = sizeof(addr); /* * We retrieve the data received */ numBytes = recvfrom(data.sock, buffer, MAX_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrLen); if (0 > numBytes) { fprintf(stderr, "Error in recvfrom(): %d %s\r\n", errno, strerror(errno)); } else if (0 < numBytes) { char s[INET6_ADDRSTRLEN]; in_port_t port; #ifdef WITH_TINYDTLS dtls_connection_t * connP; #else connection_t * connP; #endif if (AF_INET == addr.ss_family) { struct sockaddr_in *saddr = (struct sockaddr_in *)&addr; inet_ntop(saddr->sin_family, &saddr->sin_addr, s, INET6_ADDRSTRLEN); port = saddr->sin_port; } else if (AF_INET6 == addr.ss_family) { struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)&addr; inet_ntop(saddr->sin6_family, &saddr->sin6_addr, s, INET6_ADDRSTRLEN); port = saddr->sin6_port; } fprintf(stderr, "%d bytes received from [%s]:%hu\r\n", numBytes, s, ntohs(port)); /* * Display it in the STDERR */ output_buffer(stderr, buffer, numBytes, 0); connP = connection_find(data.connList, &addr, addrLen); if (connP != NULL) { /* * Let liblwm2m respond to the query depending on the context */ #ifdef WITH_TINYDTLS int result = connection_handle_packet(connP, buffer, numBytes); if (0 != result) { printf("error handling message %d\n",result); } #else lwm2m_handle_packet(lwm2mH, buffer, numBytes, connP); #endif conn_s_updateRxStatistic(objArray[7], numBytes, false); } else { fprintf(stderr, "received bytes ignored!\r\n"); } } } /* * If the event happened on the SDTIN */ else if (FD_ISSET(STDIN_FILENO, &readfds)) { numBytes = read(STDIN_FILENO, buffer, MAX_PACKET_SIZE - 1); if (numBytes > 1) { buffer[numBytes] = 0; /* * We call the corresponding callback of the typed command passing it the buffer for further arguments */ handle_command(commands, (char*)buffer); } if (g_quit == 0) { fprintf(stdout, "\r\n> "); fflush(stdout); } else { fprintf(stdout, "\r\n"); } } } } /* * Finally when the loop is left smoothly - asked by user in the command line interface - we unregister our client from it */ if (g_quit == 1) { #ifdef LWM2M_BOOTSTRAP close_backup_object(); #endif lwm2m_close(lwm2mH); } close(data.sock); connection_free(data.connList); clean_security_object(objArray[0]); lwm2m_free(objArray[0]); clean_server_object(objArray[1]); lwm2m_free(objArray[1]); free_object_device(objArray[2]); free_object_firmware(objArray[3]); free_object_location(objArray[4]); free_test_object(objArray[5]); free_object_conn_m(objArray[6]); free_object_conn_s(objArray[7]); acl_ctrl_free_object(objArray[8]); #ifdef MEMORY_TRACE if (g_quit == 1) { trace_print(0, 1); } #endif return 0; }
int lwm2m_client_cb(void *args) { int argc; char **argv; int i; int result; int opt; char *name = "testlwm2mclient"; #ifdef WITH_MBEDTLS unsigned char psk[MBEDTLS_PSK_MAX_LEN]; /* set default tls option */ /* * if you want to change auth_mode, please change 3rd parameter of tls_opt structure * - auth_mode can be configured (2: mandatory 1: optional, 0: not verify) */ tls_opt option = {MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_DATAGRAM, 0, 1, NULL, }; /* set cipher suite to all*/ option.force_ciphersuites[0] = mbedtls_ssl_get_ciphersuite_id(LWM2M_CIPHERSUIT); option.force_ciphersuites[1] = 0; #endif struct timeval tv = {60, 0}; argc = ((struct pthread_arg *)args)->argc; argv = ((struct pthread_arg *)args)->argv; lwm2mH = NULL; g_quit = 0; server = DEFAULT_SERVER_IPV4; serverPort = LWM2M_STANDARD_PORT_STR; /* Setting up the command line interface. */ command_desc_t commands[] = { {"list", "List known servers.", NULL, prv_output_servers, NULL}, { "change", "Change the value of resource.", " change URI [DATA]\r\n" " URI: uri of the resource such as /3/0, /3/0/2\r\n" " DATA: (optional) new value\r\n", prv_change, NULL }, { "update", "Trigger a registration update", " update SERVER\r\n" " SERVER: short server id such as 123\r\n", prv_update, NULL }, {"ls", "List Objects and Instances", NULL, prv_object_list, NULL}, { "dump", "Dump an Object", "dump URI" "URI: uri of the Object or Instance such as /3/0, /1\r\n", prv_object_dump, NULL }, {"add", "Add support of object 1024", NULL, prv_add, NULL}, {"rm", "Remove support of object 1024", NULL, prv_remove, NULL}, {"quit", "Quit the client gracefully.", NULL, prv_quit, NULL}, {"^C", "Quit the client abruptly (without sending a de-register message).", NULL, NULL, NULL}, COMMAND_END_LIST }; memset(&data, 0, sizeof(client_data_t)); data.addressFamily = AF_INET; opt = 1; while (opt < argc) { if (argv[opt] == NULL || argv[opt][0] != '-' || argv[opt][2] != 0) { print_usage(); return 0; } switch (argv[opt][1]) { case 't': opt++; if (opt >= argc) { print_usage(); return 0; } if (1 != sscanf(argv[opt], "%d", &g_lifetime)) { print_usage(); return 0; } break; #ifdef WITH_MBEDTLS case 'i': opt++; if (opt >= argc) { print_usage(); return 0; } g_pskId = argv[opt]; break; case 's': opt++; if (opt >= argc) { print_usage(); return 0; } g_pskBuffer = argv[opt]; break; #endif case 'h': opt++; if (opt >= argc) { print_usage(); return 0; } server = argv[opt]; break; default: print_usage(); return 0; } opt += 1; } /* Parse server URI to distinguish protocol and server address */ g_proto = coap_get_protocol_from_uri(server); if (g_proto >= COAP_PROTOCOL_MAX) { printf("Not supported protocol : %d\n", g_proto); return -1; } /* Move pointer to address field */ server += strlen(coap_uri_prefix[g_proto]); serverPort = coap_get_port_from_proto(g_proto); if (lwm2m_init_object() < 0) { } /* This call an internal function that create an socket. */ printf("Trying to bind LWM2M Client to port %s\n", serverPort); data.sock = create_socket(g_proto, serverPort, data.addressFamily); if (data.sock < 0) { fprintf(stderr, "Failed to open socket: %d %s\r\n", errno, strerror(errno)); return -1; } #ifdef WITH_MBEDTLS if (g_proto == COAP_TCP_TLS || g_proto == COAP_UDP_DTLS) { /* Set Transport layer (TCP or UDP) */ switch (g_proto) { case COAP_TCP_TLS: option.transport = MBEDTLS_SSL_TRANSPORT_STREAM; break; case COAP_UDP_DTLS: option.transport = MBEDTLS_SSL_TRANSPORT_DATAGRAM; break; default: break; } data.tls_opt = &option; /* Set credential information */ tls_cred cred; memset(&cred, 0, sizeof(tls_cred)); if (g_pskBuffer) { if (lwm2m_unhexify(psk, g_pskBuffer, &cred.psk_len) == 0) { if (g_pskId) { cred.psk_identity = g_pskId; cred.psk = psk; } } if (cred.psk_identity == NULL && cred.psk == NULL) { printf("failed to set psk info\n"); goto exit; } } else { printf("Please set psk and psk_id\n"); goto exit; } data.tls_context = TLSCtx(&cred); if (data.tls_context == NULL) { printf("TLS context initialize failed\n"); goto exit; } } #endif if ((lwm2mH = lwm2m_init(&data)) == NULL) { printf("lwm2m_init2() failed\n"); goto exit; } lwm2mH->protocol = g_proto; if ((result = lwm2m_configure(lwm2mH, name, NULL, NULL, OBJ_COUNT, objArray)) != 0) { printf("lwm2m_configure() failed: 0x%X\n", result); goto exit; } /* Register the command line command */ for (i = 0 ; commands[i].name != NULL ; i++) { commands[i].userData = (void *)lwm2mH; } printf("LWM2M Client \"%s\" started on port %s\n> ", name, serverPort); /* We now enter in a while loop that will handle the communications from the server */ while (0 == g_quit) { tv.tv_sec = 60; tv.tv_usec = 0; if ((result = lwm2m_step(lwm2mH, &(tv.tv_sec))) != 0) { printf("lwm2m_step() failed: 0x%X\n", result); goto exit; } else { printf(" -> State: %s\n", g_step[lwm2mH->state]); } fd_set readfds; FD_ZERO(&readfds); FD_SET(data.sock, &readfds); FD_SET(STDIN_FILENO, &readfds); result = select(FD_SETSIZE, &readfds, NULL, NULL, &tv); if (result < 0) { if (errno != EINTR) { fprintf(stderr, "Error in select(): %d %s\r\n", errno, strerror(errno)); } } else if (result > 0) { uint8_t buffer[MAX_PACKET_SIZE]; int numBytes; if (FD_ISSET(data.sock, &readfds)) { client_data_t *user_data = lwm2mH->userData; numBytes = connection_read(g_proto, user_data->connP, data.sock, buffer, MAX_PACKET_SIZE, NULL, 0); if (numBytes > 0) { output_buffer(stderr, buffer, numBytes, 0); lwm2m_handle_packet(lwm2mH, buffer, numBytes, user_data->connP); conn_s_updateRxStatistic(objArray[7], numBytes, false); } else { printf("received bytes ignored!\n"); } /* If the event happened on the SDTIN */ } else if (FD_ISSET(STDIN_FILENO, &readfds)) { numBytes = read_input_command_line((char *)buffer); if (numBytes > 1) { buffer[numBytes] = 0; handle_command(commands, (char *)buffer); } if (g_quit == 0) { fprintf(stdout, "\r\n> "); fflush(stdout); } } } } exit: if (g_quit && lwm2mH) { lwm2m_close(lwm2mH); sleep(1); } #ifdef WITH_MBEDTLS if (data.tls_context) { TLSCtx_free(data.tls_context); } #endif if (data.sock >= 0) { printf("Closing %d\n", data.sock); shutdown(data.sock, 3); if ((i = close(data.sock)) != 0) { printf("Fail to close %d\n", errno); } } if (data.connP) { connection_free(data.connP); } clean_security_object(objArray[0]); clean_server_object(objArray[1]); free_object_device(objArray[2]); free_object_firmware(objArray[3]); free_object_location(objArray[4]); free_test_object(objArray[5]); free_object_conn_m(objArray[6]); free_object_conn_s(objArray[7]); acl_ctrl_free_object(objArray[8]); lwm2m_free(objArray[0]); lwm2m_free(objArray[1]); return 0; }