#include #include #include #include #ifdef USE_OPENSSL #include #include #endif #include #include #include #include "utils.h" #include "handle.h" #include "ws.h" static void ws_gen_mask_key(ws_msg_header_t *header) { int r = rand(); header->mask_key[0] = (r >> 24) & 0xFF; header->mask_key[1] = (r >> 16) & 0xFF; header->mask_key[2] = (r >> 8) & 0xFF; header->mask_key[3] = r & 0xFF; } /** * Read a frame header * based on this header, we'll decide * the appropriate handle for frame data */ ws_msg_header_t *ws_read_header(void *client) { uint8_t byte = 0; uint8_t bytes[8]; ws_msg_header_t *header = (ws_msg_header_t *)malloc(sizeof(*header)); // get first byte if (antd_recv(client, &byte, sizeof(byte)) < 0) goto fail; if (BITV(byte, 6) || BITV(byte, 5) || BITV(byte, 4)) goto fail; // all RSV bit must be 0 //printf("FIN: %d, RSV1: %d, RSV2: %d, RSV3:%d, opcode:%d\n", BITV(byte,7), BITV(byte,6), BITV(byte,5), BITV(byte,4),(byte & 0x0F) ); // find and opcode header->fin = BITV(byte, 7); header->opcode = (byte & 0x0F); // get next byte if (antd_recv(client, &byte, sizeof(byte)) < 0) goto fail; //printf("MASK: %d paylen:%d\n", BITV(byte,7), (byte & 0x7F)); // check mask bit, should be 1 header->mask = BITV(byte, 7); /*if(!BITV(byte,7)) { // close the connection with protocol error ws_close(client, 1002); goto fail; }*/ // get the data length of the frame int len = (byte & 0x7F); if (len <= 125) { header->plen = len; } else if (len == 126) { if (antd_recv(client, bytes, 2 * sizeof(uint8_t)) < 0) goto fail; header->plen = (bytes[0] << 8) + bytes[1]; } else { //read only last 4 byte if (antd_recv(client, bytes, 8 * sizeof(uint8_t)) < 0) goto fail; header->plen = (bytes[4] << 24) + (bytes[5] << 16) + (bytes[6] << 8) + bytes[7]; } //printf("len: %d\n", header->plen); // last step is to get the maskey if (header->mask) if (antd_recv(client, header->mask_key, 4 * sizeof(uint8_t)) < 0) goto fail; //printf("key 0: %d key 1: %d key2:%d, key3: %d\n",header->mask_key[0],header->mask_key[1],header->mask_key[2], header->mask_key[3] ); // check wheather it is a ping or a close message // process it and return NULL //otherwise return the header //return the header switch (header->opcode) { case WS_CLOSE: // client requests to close the connection // send back a close message UNUSED(ws_send_close(client, 1000, header->mask ? 0 : 1)); //goto fail; break; case WS_PING: // client send a ping // send back a pong message UNUSED(ws_pong(client, header, header->mask ? 0 : 1)); break; default: break; } return header; fail: free(header); return NULL; } /** * Read data from client * and unmask data using the key */ int ws_read_data(void *client, ws_msg_header_t *header, int len, uint8_t *data) { // if len == -1 ==> read all remaining data to 'data'; if (header->plen == 0) return 0; int dlen = (len == -1 || len > (int)header->plen) ? (int)header->plen : len; if ((dlen = antd_recv(client, data, dlen)) < 0) return -1; header->plen = header->plen - dlen; // unmask received data if (header->mask) for (int i = 0; i < dlen; ++i) data[i] = data[i] ^ header->mask_key[i % 4]; data[dlen] = '\0'; return dlen; } int _send_header(void *client, ws_msg_header_t header) { uint8_t byte = 0; uint8_t bytes[8]; for (int i = 0; i < 8; i++) bytes[i] = 0; //first byte |FIN|000|opcode| byte = (header.fin << 7) + header.opcode; //printf("BYTE: %d\n", byte); if (antd_send(client, &byte, 1) != 1) return -1; // second byte, payload length // mask may be 0 or 1 //if(header.mask == 1) // printf("Data is masked\n"); if (header.plen <= 125) { byte = (header.mask << 7) + header.plen; if (antd_send(client, &byte, 1) != 1) return -1; } else if (header.plen < 65536) // 16 bits { byte = (header.mask << 7) + 126; bytes[0] = (header.plen) >> 8; bytes[1] = (header.plen) & 0x00FF; if (antd_send(client, &byte, 1) != 1) return -1; if (antd_send(client, bytes, 2) != 2) return -1; } else // > 16 bits { byte = (header.mask << 7) + 127; bytes[4] = (header.plen) >> 24; bytes[5] = ((header.plen) >> 16) & 0x00FF; bytes[6] = ((header.plen) >> 8) & 0x00FF; bytes[7] = (header.plen) & 0x00FF; if (antd_send(client, &byte, 1) != 1) return -1; if (antd_send(client, bytes, 8) != 8) return -1; } // send mask key if (header.mask) { if (antd_send(client, header.mask_key, 4) != 4) return -1; } return 0; } /** * Send a frame to client */ int ws_send_frame(void *client, uint8_t *data, ws_msg_header_t header) { uint8_t *masked; masked = data; int ret; if (header.mask) { ws_gen_mask_key(&header); masked = (uint8_t *)malloc(header.plen); for (int i = 0; i < (int)header.plen; ++i) masked[i] = data[i] ^ header.mask_key[i % 4]; } if (_send_header(client, header) != 0) return -1; if (header.opcode == WS_TEXT) ret = antd_send(client, (char *)masked, header.plen); else ret = antd_send(client, (uint8_t *)masked, header.plen); if (masked && header.mask) free(masked); if (ret != (int)header.plen) { return -1; } return 0; } /** * send a text data frame to client */ int ws_send_text(void *client, const char *data, int mask) { ws_msg_header_t header; header.fin = 1; header.opcode = WS_TEXT; header.mask = mask; header.plen = strlen(data); //_send_header(client,header); //send(client, data, header.plen,0); return ws_send_frame(client, (uint8_t *)data, header); } /** * send a single binary data fram to client * not tested yet, but should work */ int ws_send_binary(void *client, uint8_t *data, int l, int mask) { ws_msg_header_t header; header.fin = 1; header.opcode = WS_BIN; header.plen = l; header.mask = mask; return ws_send_frame(client, data, header); //_send_header(client,header); //send(client, data, header.plen,0); } /* * send a file as binary data */ int ws_send_file(void *client, const char *file, int mask) { uint8_t buff[1024]; FILE *ptr; ptr = fopen(file, "rb"); if (!ptr) { return ws_send_close(client, 1011, mask); } ws_msg_header_t header; size_t size; int first_frame = 1; int ret = 0; //ws_send_frame(client,buff,header); header.mask = mask; while (!feof(ptr)) { size = fread(buff, 1, 1024, ptr); if (feof(ptr)) header.fin = 1; else header.fin = 0; // clear opcode if (first_frame) { header.opcode = WS_BIN; first_frame = 0; } else header.opcode = 0; header.plen = size; //printf("FIN: %d OC:%d\n", header.fin, header.opcode); ret += ws_send_frame(client, buff, header); } fclose(ptr); if (ret != 0) { return -1; } return 0; } /** * Not tested yet * but should work */ int ws_pong(void *client, ws_msg_header_t *oheader, int mask) { ws_msg_header_t pheader; int ret; pheader.fin = 1; pheader.opcode = WS_PONG; pheader.plen = oheader->plen; pheader.mask = mask; uint8_t *data = (uint8_t *)malloc(oheader->plen); if (!data) return -1; if (ws_read_data(client, oheader, pheader.plen, data) == -1) { ERROR("Cannot read ping data %d", pheader.plen); free(data); return -1; } ret = ws_send_frame(client, data, pheader); free(data); //_send_header(client, pheader); //send(client, data, len, 0); return ret; } int ws_ping(void *client, const char *echo, int mask) { ws_msg_header_t pheader; pheader.fin = 1; pheader.opcode = WS_PING; pheader.plen = strlen(echo); pheader.mask = mask; return ws_send_frame(client, (uint8_t *)echo, pheader); } /* * Not tested yet, but should work */ int ws_send_close(void *client, unsigned int status, int mask) { //printf("CLOSED\n"); ws_msg_header_t header; header.fin = 1; header.opcode = WS_CLOSE; header.plen = 2; header.mask = mask; uint8_t bytes[2]; bytes[0] = status >> 8; bytes[1] = status & 0xFF; /*if(mask) { // XOR itself header.mask_key[0] = bytes[0]; header.mask_key[1] = bytes[1]; bytes[0] = bytes[1] ^ bytes[1]; }*/ return ws_send_frame(client, bytes, header); //_send_header(client, header); //send(client,bytes,2,0); } void ws_client_close(ws_client_t *wsclient) { antd_close(wsclient->antdsock); #ifdef USE_OPENSSL if (wsclient->ssl_ctx) { SSL_CTX_free(wsclient->ssl_ctx); FIPS_mode_set(0); // DEPRECATED: CONF_modules_unload(1); EVP_cleanup(); EVP_PBE_cleanup(); // DEPRECATED:ENGINE_cleanup(); CRYPTO_cleanup_all_ex_data(); // DEPRECATED: ERR_remove_state(0); ERR_free_strings(); } #endif } //this is for the client side, not use for now int ws_client_connect(ws_client_t *wsclient, port_config_t pcnf) { char* ip = ip_from_hostname(wsclient->host); if (ip == NULL) return -1; int sock = request_socket(ip, pcnf.port); if (sock <= 0) { ERROR("Cannot request socket"); return -1; } // time out setting struct timeval timeout; timeout.tv_sec = CONN_TIME_OUT_S; timeout.tv_usec = 0; //3 s if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) ERROR("setsockopt failed:%s", strerror(errno)); if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) ERROR("setsockopt failed:%s", strerror(errno)); // will be free wsclient->antdsock->sock = sock; wsclient->antdsock->z_status = 0; wsclient->antdsock->last_io = time(NULL); wsclient->antdsock->zstream = NULL; #ifdef USE_OPENSSL if (pcnf.usessl) { SSL_library_init(); SSL_load_error_strings(); ERR_load_crypto_strings(); OpenSSL_add_ssl_algorithms(); const SSL_METHOD *method; unsigned long ssl_err = 0; method = SSLv23_client_method(); ssl_err = ERR_get_error(); if (!method) { ERROR("SSLv23_method: %s", ERR_error_string(ssl_err, NULL)); return -1; } wsclient->ssl_ctx = SSL_CTX_new(method); ssl_err = ERR_get_error(); if (!wsclient->ssl_ctx) { ERROR("SSL_CTX_new: %s", ERR_error_string(ssl_err, NULL)); return -1; } // configure the context #if defined(SSL_CTX_set_ecdh_auto) SSL_CTX_set_ecdh_auto(wsclient->ssl_ctx, 1); #else SSL_CTX_set_tmp_ecdh(wsclient->ssl_ctx, EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); #endif SSL_CTX_set_options(wsclient->ssl_ctx, SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_SSLv2 | SSL_OP_NO_TICKET); // set the cipher suit const char *suit = wsclient->ciphersuit ? wsclient->ciphersuit : PREFERRED_WS_CIPHERS; //const char* suit = "AES128-SHA"; if (SSL_CTX_set_cipher_list(wsclient->ssl_ctx, suit) != 1) { ssl_err = ERR_get_error(); // TODO Close the context ERROR("SSL_CTX_set_cipher_list: %s", ERR_error_string(ssl_err, NULL)); return -1; } if (wsclient->sslcert && wsclient->sslkey) { if (SSL_CTX_use_certificate_file(wsclient->ssl_ctx, wsclient->sslcert, SSL_FILETYPE_PEM) <= 0) { ssl_err = ERR_get_error(); ERROR("SSL_CTX_use_certificate_file: %s", ERR_error_string(ssl_err, NULL)); return -1; } if (wsclient->sslpasswd) SSL_CTX_set_default_passwd_cb_userdata(wsclient->ssl_ctx, (void *)wsclient->sslpasswd); if (SSL_CTX_use_PrivateKey_file(wsclient->ssl_ctx, wsclient->sslkey, SSL_FILETYPE_PEM) <= 0) { ssl_err = ERR_get_error(); ERROR("SSL_CTX_use_PrivateKey_file: %s", ERR_error_string(ssl_err, NULL)); return -1; } if (SSL_CTX_check_private_key(wsclient->ssl_ctx) == 0) { ssl_err = ERR_get_error(); ERROR("SSL_CTX_check_private_key: %s", ERR_error_string(ssl_err, NULL)); return -1; } } // // validate if (wsclient->verify_location) { SSL_CTX_set_verify(wsclient->ssl_ctx, SSL_VERIFY_PEER, NULL); SSL_CTX_set_verify_depth(wsclient->ssl_ctx, 5); if (!SSL_CTX_load_verify_locations(wsclient->ssl_ctx, wsclient->verify_location, NULL)) { ssl_err = ERR_get_error(); // TODO Close the context ERROR("SSL_CTX_load_verify_locations: %s", ERR_error_string(ssl_err, NULL)); return -1; } } else { SSL_CTX_set_verify(wsclient->ssl_ctx, SSL_VERIFY_NONE, NULL); } wsclient->antdsock->ssl = (void *)SSL_new(wsclient->ssl_ctx); if (!wsclient->antdsock->ssl) { ssl_err = ERR_get_error(); ERROR("SSL_new: %s", ERR_error_string(ssl_err, NULL)); return -1; } SSL_set_fd((SSL *)wsclient->antdsock->ssl, wsclient->antdsock->sock); int stat, ret; ERR_clear_error(); while ((ret = SSL_connect(wsclient->antdsock->ssl)) <= 0) { stat = SSL_get_error(wsclient->antdsock->ssl, ret); switch (stat) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: case SSL_ERROR_NONE: continue; default: ERR_print_errors_fp(stderr); ERROR("Error performing SSL handshake %d", stat); return -1; } } } #endif return 0; } int ws_open_handshake(ws_client_t *client) { char buf[MAX_BUFF]; // now send ws request handshake sprintf(buf, CLIENT_RQ, client->resource, client->host); //printf("Send %s\n", buf); int size = antd_send(client->antdsock, buf, strlen(buf)); if (size != (int)strlen(buf)) { ERROR("Cannot send request \n"); return -1; } // now verify if server accept the socket size = read_buf(client->antdsock, buf, MAX_BUFF); char *token; int done = 0; while (size > 0 && strcmp("\r\n", buf)) { char *line = buf; token = strsep(&line, ":"); trim(token, ' '); if (token != NULL && strcasecmp(token, "Sec-WebSocket-Accept") == 0) { token = strsep(&line, ":"); trim(token, ' '); trim(token, '\n'); trim(token, '\r'); if (strcasecmp(token, SERVER_WS_KEY) == 0) { //LOG("Handshake sucessfull\n"); done = 1; } else { ERROR("WS handshake, Wrong key %s vs %s", token, SERVER_WS_KEY); return -1; } } //if(line) free(line); size = read_buf(client->antdsock, buf, MAX_BUFF); } if (done) return 0; return -1; } char *get_ip_address() { struct ifaddrs *addrs; getifaddrs(&addrs); struct ifaddrs *tmp = addrs; char *ip; while (tmp) { if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) { struct sockaddr_in *pAddr = (struct sockaddr_in *)tmp->ifa_addr; ip = inet_ntoa(pAddr->sin_addr); if (strcmp(ip, "127.0.0.1") != 0) return ip; } tmp = tmp->ifa_next; } freeifaddrs(addrs); return "127.0.0.1"; }