/* (c) Copyright 2012 - 2018 Anton Sviridenko https://picapica.im This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include "../PICA_client.h" #include "../PICA_proto.h" struct sockaddr_in a; struct PICA_acc *acc; struct PICA_c2n *c; struct PICA_c2c *chn; enum PICA_directc2c_config directc2c_cfg = PICA_DIRECTC2C_CFG_DISABLED; struct PICA_listener *l = NULL; int _outgoing_chnl = 0; int connected_to_node = 0; int c2c_active = 0; int c2c_in_progress = 0; int echo_mode = 0; int random_message_mode = 0; int delivery_acks_count = 0; int rmm_wait_for_reply = 0; unsigned char buf[64 * 1024]; int accept_cb(const unsigned char *id) { printf("accept_cb: %s\n", PICA_id_to_base64(id, NULL)); return 1; } //получение сообщения. void newmsg_cb(const unsigned char *peer_id, const char* msgbuf, unsigned int nb, int type) { if (echo_mode) { int ret; ret = PICA_send_msg(chn, msgbuf, nb); if (ret != PICA_OK) printf("failed to send message, error = %i\n", ret); return; } if (random_message_mode) { //compare received & sent messages, fail if they do not match exactly if (memcmp(msgbuf, buf, nb) != 0) { printf("sent and returned messages are not equal!\n"); exit(1); } rmm_wait_for_reply = 0; } memcpy(buf, msgbuf, nb); buf[nb] = 0; printf(" %s: ", PICA_id_to_base64(peer_id, NULL)); puts(buf); } //получение подтверждения о доставке сообщения void msgok_cb(const unsigned char *peer_id) { delivery_acks_count++; puts("[V]"); } //создание канала с собеседником void c2c_established_cb(const unsigned char *peer_id) { c2c_active = 1; c2c_in_progress = 0; puts("c2c_established_cb"); chn = c->chan_list_head;//hack, its better to pass new c2c pointer as callback arg } void c2c_failed_cb(const unsigned char *peer_id) { c2c_in_progress = 0; printf("Failed to create c2c to %s\n", PICA_id_to_base64(peer_id, NULL)); chn = 0; } //входящий запрос на создание канала от пользователя с номером caller_id //<acc->id, NULL)); } void c2n_failed_cb(struct PICA_c2n *c2n, int error) { printf("c2n_failed_cb: %p error_code %i\n", c2n, error); c = NULL; } void c2n_closed_cb(struct PICA_c2n *c2n, int error) { printf("c2n_closed_cb: %p error_code %i\n", c2n, error); c = NULL; } void listener_error(struct PICA_listener *lst, int errorcode) { printf("listener_error: %p error code %i\n", lst, errorcode); l = NULL; } void multilogin_cb(time_t timestamp, void *addr_bin, const char *addr_str, uint16_t port) { printf("received MULTILOGIN\n"); } struct PICA_client_callbacks cbs = { newmsg_cb, msgok_cb, c2c_established_cb, c2c_failed_cb, accept_cb, notfound_cb, c2c_closed_cb, nodelist_cb, peer_cert_verify_cb, accept_file_cb, accepted_file_cb, denied_file_cb, file_progress, file_control, file_finished, c2n_established_cb, c2n_failed_cb, c2n_closed_cb, listener_error, multilogin_cb }; int main(int argc, char** argv) { int ret, opt; unsigned char peer_id[PICA_ID_SIZE]; struct PICA_c2n* c2ns[] = {NULL, NULL}; fd_set stdinfds; struct timeval tv; const char *nodeaddr = NULL, *nodeport = NULL, *certfile = NULL, *peerid = NULL, *localdirectaddr = NULL, *extport = NULL, *filetosend = NULL, *locport = NULL; int multilogin = PICA_MULTILOGIN_PROHIBIT; while ((opt = getopt(argc, argv, "a:p:c:i:d:e:l:f:tmrM:")) != -1) { switch(opt) { case 'a': nodeaddr = optarg; break; case 'p': nodeport = optarg; break; case 'c': certfile = optarg; break; case 'i': peerid = optarg; break; case 'd': localdirectaddr = optarg; break; case 'e': extport = optarg; break; case 'f': filetosend = optarg; break; case 'l': locport = optarg; break; case 't': directc2c_cfg = PICA_DIRECTC2C_CFG_CONNECTONLY; break; case 'm': echo_mode = 1; break; case 'r': random_message_mode = 1; break; case 'M': if (strcmp(optarg, "prohibit") == 0) multilogin = PICA_MULTILOGIN_PROHIBIT; else if (strcmp(optarg, "replace") == 0) multilogin = PICA_MULTILOGIN_REPLACE; else if (strcmp(optarg, "sync") == 0) multilogin = PICA_MULTILOGIN_ALLOW; else { fprintf(stderr, "invalid multilogin option argument: %s\n", optarg); return -1; } break; default: fprintf(stderr, "invalid arg %s\n", optarg); return -1; } } if (echo_mode && random_message_mode) { puts("echo mode and random test messages mode are mutually exclusive, please select only one of them"); return -1; } if (argc < 4 || !nodeaddr || !nodeport || !certfile) { puts("usage: test_client -a node address -p node port -c cert_filename [-i peer id]\n\ -d local address for direct incoming c2c connections\n\ -e external port for incoming direct connections\n\ -l internal port to listen for incoming direct connections\n\ -t enable connect-only mode for direct c2c connections\n\ -m start in echo mode, received messages are sent back, input from stdin is not accepted\n\ -r send random test messages and exit, expects other side to be run in echo mode\n\ -M multilogin policy, one from \"prohibit\", \"replace\", \"sync\"\n\ cert_filename should point to file that contains client certificate, private key and Diffie-Hellman parameters in PEM format"); return 0; } puts(SSLeay_version(SSLEAY_VERSION)); printf("Calling PICA_init...\n"); PICA_client_init(&cbs); printf("opening account..."); ret = PICA_open_acc(certfile, certfile, certfile, NULL, &acc); if (ret != PICA_OK) { fprintf(stderr, "failed to open account, ret = %i\n", ret); return 1; } if (localdirectaddr) { int intp = 2300, extp = 2300; directc2c_cfg = PICA_DIRECTC2C_CFG_ALLOWINCOMING; if (extport) extp = atoi(extport); if (locport) intp = atoi(locport); printf("new listener on %s port %i external announced port %i\n", localdirectaddr, intp, extp); ret = PICA_new_listener(acc, localdirectaddr, extp, intp, &l); if (ret != PICA_OK) { fprintf(stderr, "failed to create listener, error code: %i\n", ret); l = NULL; } } printf("making connection...\n"); //PICA_new_c2n(const char *nodeaddr, unsigned int port, const char *CA_file, const char *cert_file, const char *pkey_file, const char* password, struct PICA_c2n **ci) ret = PICA_new_c2n(acc, nodeaddr, atoi(nodeport), directc2c_cfg, multilogin, l, &c); ERR_print_errors_fp(stdout); if (ret == PICA_OK) printf("PICA_OK started c2n connection to node...\n"); else { printf("PICA_new_c2n: ret=%i\n", ret); return 1; } c2ns[0] = c; while(PICA_event_loop(c2ns, 500) == PICA_OK && c != NULL) { //puts("event loop..."); if (peerid && connected_to_node == 1 && c2c_active == 0 && c2c_in_progress == 0) { if (PICA_id_from_base64(peerid, peer_id) == NULL) { puts("invalid peer_id"); return 1; } printf("Creating c2c to %s...\n", PICA_id_to_base64(peer_id, NULL)); ret = PICA_new_c2c(c, peer_id, l, &chn); //sleep(17);//timeout test if (ret != PICA_OK) { printf("error: ret=%i\n", ret); return 1; } printf("chn = %p\n", chn); c2c_in_progress = 1; } if (random_message_mode && connected_to_node == 1 && c2c_active == 1 && rmm_wait_for_reply == 0) { static int test_stage = 0; switch(test_stage) { //send zero-length message case 0: ret = PICA_send_msg(chn, NULL, 0); test_stage++; rmm_wait_for_reply = 1; break; //send max length message case 1: memset(buf, 'X', PICA_PROTO_C2CMSG_MAXDATASIZE); ret = PICA_send_msg(chn, buf, PICA_PROTO_C2CMSG_MAXDATASIZE); test_stage++; rmm_wait_for_reply = 1; break; //send random length message case 2: RAND_pseudo_bytes((unsigned char*)&ret, sizeof ret); if (ret < 0) ret = 0 - ret; ret %= PICA_PROTO_C2CMSG_MAXDATASIZE; memset(buf, 'x', ret); ret = PICA_send_msg(chn, buf, ret); test_stage++; rmm_wait_for_reply = 1; break; default: puts("waiting for test messages to be delivered"); } if (ret != PICA_OK) { printf("failed to send message, error = %i\n", ret); return 1; } if (delivery_acks_count == 3) break;//exit main loop on success } if (connected_to_node == 1 && c2c_active == 1 && !echo_mode && !random_message_mode) { FD_ZERO(&stdinfds); FD_SET(STDIN_FILENO, &stdinfds); tv.tv_sec = 0; tv.tv_usec = 500000; ret = select(STDIN_FILENO + 1, &stdinfds, NULL, NULL, &tv); if (ret == -1) { perror("select() on stdin failed"); return 1; } if (ret > 0 && FD_ISSET(STDIN_FILENO, &stdinfds)) { ret = read(STDIN_FILENO, buf, 256); if (ret <= 0) { perror("read from stdin"); return 1; } ret = PICA_send_msg(chn, buf, ret); if (ret != PICA_OK) printf("failed to send message, error = %i\n", ret); } } if (connected_to_node == 1 && c2c_active == 1 && filetosend) { printf("sending file %s to peer...\n", filetosend); ret = PICA_send_file(chn, filetosend); if (ret != PICA_OK) printf("failed to send file %s, error = %i\n", filetosend, ret); filetosend = NULL; } } if (c) PICA_close_c2n(c); if(l) PICA_close_listener(l); return 0; }