Repository: hamidreza-s/NanoChat Branch: master Commit: 48d1c38eb113 Files: 34 Total size: 45.4 KB Directory structure: gitextract_fx0k1tao/ ├── .gitignore ├── Makefile.am ├── README.md ├── TODO.md ├── configure.ac ├── lib/ │ ├── base64/ │ │ ├── .gitignore │ │ └── Makefile.am │ ├── nanomsg/ │ │ ├── .gitignore │ │ └── Makefile.am │ ├── parson/ │ │ ├── .gitignore │ │ └── Makefile.am │ ├── sodium/ │ │ ├── .gitignore │ │ └── Makefile.am │ └── vedis/ │ ├── .gitignore │ └── Makefile.am └── src/ ├── Makefile.am ├── nc.c ├── nc.h ├── nc_array.c ├── nc_conf.c ├── nc_crypto.c ├── nc_dal.c ├── nc_disco.c ├── nc_json.c ├── nc_log.c ├── nc_msg.c ├── nc_names.c ├── nc_netif.c ├── nc_otoc.c ├── nc_param.c ├── nc_rpc.c ├── nc_shell.c ├── nc_udp.c └── nc_utils.c ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .emacs nanochat var /autom4te.cache/ /config/ Makefile Makefile.in aclocal.m4 app.info compile config.h config.h.in config.log config.status configure configure.scan coverage_report depcomp install-sh libtool m4 missing stamp-h1 *~ *.html *.log *.o *.la *.so* *.a .deps *.zip *.lo *.gcno *.gcda config.guess config.sub ================================================ FILE: Makefile.am ================================================ SUBDIRS = lib/nanomsg lib/vedis lib/sodium lib/parson lib/base64 src install-data-hook: @echo bin dir $(bindir) @echo "=============================================================" @echo "| NanoChat was successfully installed " @echo "-------------------------------------------------------------" @echo "| NanoChat was installed in $(bindir) directory. " @echo "| Just type 'nanochat --help' there to get started." @echo "=============================================================" ================================================ FILE: README.md ================================================ NanoChat ===== NanoChat is a **peer-to-peer**, **end-to-end encrypted** and **discoverable** chat application that can be used inside command line. It is totally server-less and every peer can discover other peers in the same subnet without having the network address of them in advance. It can be compiled and works on both **Linux** and **OSX** system platforms. How to use ----- Call `nanochat` with desired flags. In order to see the list of available flags, use `--help` as follows: ``` NanoChat help ------------- Avaliable flags: Help: -h --help Host IP: -H --host {host IP} Broadcast IP: -B --broadcast {broadcast IP} RPC Port: -P --port {port} Discoverable: -d --discoverable Secure: -s --secure ``` - `--host {host IP}` flag is used to specify the network interface IP which you want to use. By default it is selected by NanoChat. - `--broadcast {broadcast IP}` flag is used to specify the network interface broadcast IP which you want to use. By default it is selected by NanoChat. - `--port` flag is used to specify the RPC port which is listening to answer chat requests after creating the requested chat room. Its default value is `1991`. - `--discoverable` flag is used to let NanoChat to be disovered by other peers, otherwise it doesn't answer to discovery packet and remains hidden from others. - `--secure` flag is used to make NanoChat secure for end to end message encryption with asymmetric/public-key cryptography. ----- In order to see available commands inside NanoChat use `/help` command as follows: ```shell $ nanochat --discoverable NanoChat shell was started. >> /help Available commands: /help prints this text /probe find online peers /list peers list online peers /list rooms list availabe rooms /connect {host} {port} connect to remote client /attach {room} attach to room /quit quit nanochat console >> ... ``` - `/probe` command discovers the subnet for other online NanoChat peers. - `/list peers` command list discovered online peers' host and port address. - `/list rooms` command list available rooms. - `/connect {host} {port}` command connects you to other peers. - `/attach {room}` command is used to enter to a created room by other peers. - `/quit` command closes the NanoChat console. Installation ----- NanoChat uses [GNU Autotools](https://en.wikipedia.org/wiki/GNU_Build_System). So you need to have them installed in your system. Also it needs [GCC](https://en.wikipedia.org/wiki/GNU_Compiler_Collection) or [Clang](https://en.wikipedia.org/wiki/Clang), [GNU Make](https://www.google.co.uk/?ion=1&espv=2#q=gnu%20make) and [GNU Readline](https://en.wikipedia.org/wiki/GNU_Readline) library. Other dependencies are included in the `NanoChat/lib` directory and will be compiled and linked with autotools automatically. So there is no need to get and build it manually. **Debian dependencies:** ``` $ apt-get install gcc make automake autoconf libreadline-dev ``` **Fedora dependencies:** ``` $ yum install gcc make automake autoconf libreadline-devel ``` **OSX dependencies:** ``` $ port install gcc make automake autoconf readline ``` After making sure that your system has required dependencies, clone the repo and follow installation steps: ``` $ git clone https://github.com/hamidreza-s/NanoChat.git $ cd NanoChat $ autoreconf -i $ ./configure $ make && make install ... ============================================================= | NanoChat was successfully installed ------------------------------------------------------------- | NanoChat was installed in /usr/local/bin directory. | Just type 'nanochat --help' there to get started. ============================================================= ``` Now `nanochat` executable file is accessible in the path of your shell. How it works ----- My main purpose for writing NanoChat is learning things, so I think it is good to know for you how it works and what tools and protocols it uses. - For discovering other online peers in the same subnet it uses raw UDP broadcasting. - For inter-node communication it uses some [libnanomsg](http://nanomsg.org) scalibility protocols, for instance REQREP protocol for RPC and PAIR for one to one chat. - For user's input/output multiplexing it uses [select](https://en.wikipedia.org/wiki/Select_(Unix)) POSIX-compliant syscall. - For storing user's information it uses [libvedis](https://vedis.symisc.net) embedded datastore engine. - For line-editing and history capabilities of commands it uses [libreadline](https://en.wikipedia.org/wiki/GNU_Readline). - For end to end encryption it uses [libsodium](https://github.com/jedisct1/libsodium) public-key cryptography. - For message serialization it uses [libparson](https://github.com/kgabis/parson) which is a JSON parser. - For encoding and decoding encrypted messages before sending over wire it uses base64 codec. Contribution ----- Comments, contributions and patches are greatly appreciated. License ----- The MIT License (MIT). ================================================ FILE: TODO.md ================================================ TODO ===== - peer rpc (req/rep protocol) - discovery (udp broadcasting) - one to one chat (pair protocol) - group chat (bus protocol) - integrated error handling - integrated logging and debug mode - include libnanomsg in deps directory - use autotools to be portable - use libreadline and ncurses to be TUI ================================================ FILE: configure.ac ================================================ AC_PREREQ([2.69]) AC_INIT([nanochat], [0.1], [hamidreza.s@gmail.com]) AM_INIT_AUTOMAKE([foreign]) NANOMSG_VERSION=0.8-beta AC_SUBST([NANOMSG_VERSION]) VEDIS_VERSION=1.2.6 AC_SUBST([VEDIS_VERSION]) SODIUM_VERSION=1.0.9 AC_SUBST([SODIUM_VERSION]) PARSON_VERSION=1.0.0 AC_SUBST([PARSON_VERSION]) BASE64_VERSION=1.0.0 AC_SUBST([BASE64_VERSION]) AC_PROG_CC AC_LANG([C]) AC_HEADER_STDC AC_CHECK_HEADERS([stdio.h stdlib.h unistd.h getopt.h string.h time.h \ sys/select.h stdarg.h pthread.h pwd.h sys/socket.h \ arpa/inet.h ifaddrs.h]) AC_TYPE_SIZE_T AC_CONFIG_FILES([lib/nanomsg/Makefile \ lib/vedis/Makefile \ lib/sodium/Makefile \ lib/parson/Makefile \ lib/base64/Makefile \ src/Makefile \ Makefile]) AC_CANONICAL_HOST case "${host_os}" in *linux*) # Define on Linux to enable all library features NC_OS_SPECIFIC_LDADD="-l:libnanomsg.a -l:libvedis.a -l:libsodium.a -l:libparson.a -l:libbase64.a" NC_EXTRA_LDADD=-lanl ;; *freebsd*) # Define on FreeBSD to enable all library features NC_OS_SPECIFIC_LDADD="-lnanomsg -lvedis -lsodium -lparson -lbase64" NC_EXTRA_LDADD= ;; *darwin*) # Define on Darwin to enable all library features NC_OS_SPECIFIC_LDADD="-lnanomsg -lvedis -lsodium -lparson -lbase64" NC_EXTRA_LDADD= ;; *) AC_MSG_ERROR([unsupported system: ${host_os}.]) ;; esac AC_SUBST([NC_OS_SPECIFIC_LDADD]) AC_SUBST([NC_EXTRA_LDADD]) AC_OUTPUT ================================================ FILE: lib/base64/.gitignore ================================================ base64-1.0.0 ================================================ FILE: lib/base64/Makefile.am ================================================ all-local: @if [ ! -f ./base64-$(BASE64_VERSION)/lib/libbase64.a ]; then \ tar -zxf ./base64-$(BASE64_VERSION).tar.gz && \ cd ./base64-$(BASE64_VERSION)/src && \ gcc -c base64.c -I../include && \ ar -cvq libbase64.a base64.o && \ cd .. && \ mkdir -p lib && \ mv src/libbase64.a lib/libbase64.a; \ fi; clean-local: rm -f ./base64-$(BASE64_VERSION)/src/base64.o uninstall-local: rm -rf ./base64-$(BASE64_VERSION) ================================================ FILE: lib/nanomsg/.gitignore ================================================ nanomsg-0.8-beta ================================================ FILE: lib/nanomsg/Makefile.am ================================================ all-local: @if [ ! -f ./nanomsg-$(NANOMSG_VERSION)/lib/libnanomsg.a ]; then \ tar -zxf ./nanomsg-$(NANOMSG_VERSION).tar.gz && \ cd ./nanomsg-$(NANOMSG_VERSION) && \ ./configure --prefix=`pwd` && \ make && \ make install; \ fi; clean-local: cd ./nanomsg-$(NANOMSG_VERSION) && \ make clean uninstall-local: cd ./nanomsg-$(NANOMSG_VERSION) && \ make uninstall ================================================ FILE: lib/parson/.gitignore ================================================ parson-1.0.0 ================================================ FILE: lib/parson/Makefile.am ================================================ all-local: @if [ ! -f ./parson-$(PARSON_VERSION)/lib/libparson.a ]; then \ tar -zxf ./parson-$(PARSON_VERSION).tar.gz && \ cd ./parson-$(PARSON_VERSION)/src && \ gcc -c parson.c -I../include && \ ar -cvq libparson.a parson.o && \ cd .. && \ mkdir -p lib && \ mv src/libparson.a lib/libparson.a; \ fi; clean-local: rm -f ./parson-$(PARSON_VERSION)/src/parson.o uninstall-local: rm -rf ./parson-$(PARSON_VERSION) ================================================ FILE: lib/sodium/.gitignore ================================================ libsodium-1.0.9 ================================================ FILE: lib/sodium/Makefile.am ================================================ all-local: @if [ ! -f ./libsodium-$(SODIUM_VERSION)/lib/libsodium.a ]; then \ tar -zxf ./libsodium-$(SODIUM_VERSION).tar.gz && \ cd ./libsodium-$(SODIUM_VERSION) && \ ./configure --prefix=`pwd` && \ make && \ make install; \ fi; clean-local: cd ./libsodium-$(SODIUM_VERSION) && \ make clean uninstall-local: cd ./libsodium-$(SODIUM_VERSION) && \ make uninstall ================================================ FILE: lib/vedis/.gitignore ================================================ vedis-1.2.6 ================================================ FILE: lib/vedis/Makefile.am ================================================ all-local: @if [ ! -f ./vedis-$(VEDIS_VERSION)/lib/libvedis.a ]; then \ tar -zxf ./vedis-$(VEDIS_VERSION).tar.gz && \ cd ./vedis-$(VEDIS_VERSION)/src && \ gcc -c vedis.c -I../include && \ ar -cvq libvedis.a vedis.o && \ cd .. && \ mkdir -p lib && \ mv src/libvedis.a lib/libvedis.a; \ fi; clean-local: rm -f ./vedis-$(VEDIS_VERSION)/src/vedis.o uninstall-local: rm -rf ./vedis-$(VEDIS_VERSION) ================================================ FILE: src/Makefile.am ================================================ all-local: mkdir -p $(top_srcdir)/var rm -f $(top_srcdir)/var/nanochat.conf echo "log_file=./var/nanochat.log" >> $(top_srcdir)/var/nanochat.conf touch $(top_srcdir)/var/nanochat.log clean-local: rm -f $(top_srcdir)/var/nanochat.conf rm -f $(top_srcdir)/var/nanochat.log AM_CFLAGS = -Wall AM_CFLAGS += -I$(top_srcdir)/lib/nanomsg/nanomsg-$(NANOMSG_VERSION)/include AM_CFLAGS += -L$(top_srcdir)/lib/nanomsg/nanomsg-$(NANOMSG_VERSION)/lib AM_CFLAGS += -I$(top_srcdir)/lib/vedis/vedis-$(VEDIS_VERSION)/include AM_CFLAGS += -L$(top_srcdir)/lib/vedis/vedis-$(VEDIS_VERSION)/lib AM_CFLAGS += -I$(top_srcdir)/lib/sodium/libsodium-$(SODIUM_VERSION)/include AM_CFLAGS += -L$(top_srcdir)/lib/sodium/libsodium-$(SODIUM_VERSION)/lib AM_CFLAGS += -I$(top_srcdir)/lib/parson/parson-$(PARSON_VERSION)/include AM_CFLAGS += -L$(top_srcdir)/lib/parson/parson-$(PARSON_VERSION)/lib AM_CFLAGS += -I$(top_srcdir)/lib/base64/base64-$(BASE64_VERSION)/include AM_CFLAGS += -L$(top_srcdir)/lib/base64/base64-$(BASE64_VERSION)/lib bin_PROGRAMS = nanochat nanochat_SOURCES = \ nc.h \ nc.c \ nc_param.c \ nc_netif.c \ nc_utils.c \ nc_names.c \ nc_udp.c \ nc_disco.c \ nc_rpc.c \ nc_shell.c \ nc_otoc.c \ nc_conf.c \ nc_log.c \ nc_dal.c \ nc_array.c \ nc_crypto.c \ nc_json.c \ nc_msg.c nanochat_LDADD = -lpthread -lreadline nanochat_LDADD += $(NC_OS_SPECIFIC_LDADD) nanochat_LDADD += $(NC_EXTRA_LDADD) ================================================ FILE: src/nc.c ================================================ #include "nc.h" int main(int argc, char **argv) { nc_opts opts; /* @NOTE: conf and log must start first respectively */ nc_conf_start(); nc_log_start(); nc_param_get_opts(&opts, argc, argv); nc_dal_start(&opts); nc_disco_start(&opts); nc_rpc_start(&opts); nc_crypto_start(&opts); nc_shell_start(&opts); return 0; } ================================================ FILE: src/nc.h ================================================ #ifndef NC_H #define NC_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DISCO_PORT "1881" /* not changable */ #define DEFAULT_RPC_PORT "1991" #define INIT_OTOC_PORT 1992 #define HOST_MAX 20 #define URL_MAX 30 #define PORT_MAX 6 #define OTOC_PORT_LEN 6 #define NOW_STR_LEN 26 #define CONF_KEY_LEN 1024 #define CONF_VAL_LEN 1024 /* shell commands */ #define SCMD_NAME_LEN 32 #define SCMD_MAX 10 /* otoc shell commands */ #define OCMD_NAME_LEN 32 #define OCMD_MAX 10 /* discovery */ #define DCMD_BUF 64 #define DCMD_LEN 8 #define DCMD_CODE_LEN 2 #define DCMD_PROBE_ACK_CODE "00" #define DCMD_PROBE_REQUEST_CODE "01" #define DCMD_PROBE_RESPONSE_CODE "02" #define DCMD_PROBE_TIMEOUT_SEC 3 /* rpc commands */ #define RCMD_LEN 2 #define RCMD_OTOC "01" /* one to one message types */ #define OTOC_MTYPE_LEN 8 #define OTOC_MTYPE_PKEY "publ_key" /* public key */ #define OTOC_MTYPE_RTXT "text_raw" /* raw text */ #define OTOC_MTYPE_STXT "text_sec" /* encrypted text */ #define OTOC_MTYPE_RFLE "file_raw" /* raw file */ #define OTOC_MTYPE_SFLE "file_sec" /* encrypted file */ /* names */ #define USERNAME_MAX 32 #define HOSTNAME_MAX 32 /* room */ #define ROOMS_LIMIT 16 /* global variablea */ extern unsigned char my_publickey[crypto_box_PUBLICKEYBYTES]; extern unsigned char my_secretkey[crypto_box_SECRETKEYBYTES]; extern unsigned char peers_publickey [ROOMS_LIMIT] [crypto_box_PUBLICKEYBYTES]; typedef struct nc_opts { char host[HOST_MAX]; char broadcast[HOST_MAX]; char port[PORT_MAX]; /* RPC port */ char url[URL_MAX]; int secure; int verbose; int discoverable; } nc_opts; typedef struct nc_conf_rec { char key[CONF_KEY_LEN]; char val[CONF_VAL_LEN]; } nc_conf_rec; typedef struct nc_shell_cmd { int code; char name[SCMD_NAME_LEN]; int (*func)(char *cmd, nc_opts *opts); } nc_shell_cmd; typedef struct nc_otoc_cmd { int code; char name[OCMD_NAME_LEN]; int (*func)(char *cmd); } nc_otoc_cmd; typedef struct nc_array { char **array_ptr; int array_len; int string_len; } nc_array; /* --- parameters --- */ void nc_param_get_opts(nc_opts *opts, int argc, char **argv); /* --- logger --- */ void nc_log_start(); void nc_log_write(const char *tag, const char *msg); void nc_log_writef(const char *tag, const char *msgf, ...); /* --- utilities --- */ void nc_utils_print_help(); int nc_utils_count_char(const char *str, const char chr); void nc_utils_now_str(char *time_str); void nc_utils_del_new_line(char *str); int nc_utils_has_new_line(char *str); void nc_utils_make_url(char *url, char *host, char *port); int nc_utils_get_rec_sockfd(int sock); void nc_utils_empty_string(char *str); void nc_utils_die(char *str); /* --- disco --- */ void nc_disco_start(nc_opts *opts); int nc_disco_probe(nc_opts *opts); /* --- rpc --- */ void nc_rpc_start(nc_opts *opts); /* --- shell --- */ void nc_shell_start(nc_opts *opts); void nc_shell_register_cmd(char *name, int (*func)(char *cmd, nc_opts *opts)); /* --- one to one chat --- */ void nc_otoc_start(nc_opts *opts, int pair_raw_sock); void nc_otoc_register_cmd(char *name, int (*func)(char *cmd)); /* --- config --- */ void nc_conf_start(); void nc_conf_get(nc_conf_rec *conf_rec); /* --- network interfaces --- */ int nc_netif_get_addrs(char *inet, char *broadcast); /* --- udp --- */ int nc_udp_send(char *ip, char *port, char *body, int broadcast); /* --- names --- */ int nc_names_get_hostname(char *buf, int len); int nc_names_get_username(char *buf, int len); /* --- data access layer --- */ void nc_dal_start(nc_opts *opts); int nc_dal_save_room(int room_code); int nc_dal_save_peer(char **record); int nc_dal_print_peers(); int nc_dal_print_rooms(); /* --- array --- */ nc_array* nc_array_string_new(int array_len, int string_len); int nc_array_string_set(nc_array *array, int index, char *string); char* nc_array_string_get(nc_array *array, int index); int nc_array_string_len(nc_array *array); /* --- crypto --- */ void nc_crypto_start(nc_opts *opts); /* --- json --- */ int nc_json_extract_otoc_msg(char **msg, char **type, int *original_body_len, char **body); int nc_json_make_otoc_msg(char **type, char **body, int original_body_len, char **msg); /* --- message --- */ void nc_msg_encode(int head_len, int body_len, char *head, char *body, char **buf); void nc_msg_free(char *buf); #endif ================================================ FILE: src/nc_array.c ================================================ #include "nc.h" nc_array* nc_array_string_new(int array_len, int string_len) { int i; char **array_ptr = (char**) malloc(array_len * sizeof(char**)); for(i = 0; i < array_len; i++) { array_ptr[i] = (char*) malloc(string_len * sizeof(char)); } nc_array *array = (nc_array*) malloc(sizeof(nc_array*)); array->array_ptr = array_ptr; array->array_len = array_len; array->string_len = string_len; return array; } int nc_array_string_set(nc_array *array, int index, char *string) { strncpy(array->array_ptr[index], string, array->string_len); return 0; } char* nc_array_string_get(nc_array *array, int index) { return array->array_ptr[index]; } int nc_array_string_len(nc_array *array) { return array->array_len; } int nc_array_string_free(nc_array *array) { int i; for(i = 0; i < array->array_len; i++) { free(array->array_ptr[i]); } free(array->array_ptr); return 0; } ================================================ FILE: src/nc_conf.c ================================================ #include "nc.h" static FILE *conf_fp; void nc_conf_start() { conf_fp = fopen("./var/nanochat.conf", "r"); } void nc_conf_get(nc_conf_rec *conf_rec) { char *buf = NULL; size_t buf_size = CONF_KEY_LEN + CONF_VAL_LEN + 2; while(getline(&buf, &buf_size, conf_fp) != -1) { char *key_val; if((key_val = strstr(buf, conf_rec->key)) != NULL) { strcpy(conf_rec->val, strchr(buf, '=') + 1); nc_utils_del_new_line(conf_rec->val); break; } } } ================================================ FILE: src/nc_crypto.c ================================================ #include "nc.h" unsigned char my_publickey[crypto_box_PUBLICKEYBYTES]; unsigned char my_secretkey[crypto_box_SECRETKEYBYTES]; unsigned char peers_publickey [ROOMS_LIMIT] [crypto_box_PUBLICKEYBYTES]; void nc_crypto_start(nc_opts *opts) { if(!opts->secure) return; nc_log_writef("info", "Crypto mode has been started."); crypto_box_keypair(my_publickey, my_secretkey); } ================================================ FILE: src/nc_dal.c ================================================ #include "nc.h" #define ROOMS_HASH_TABLE "otoc" #define PEERS_HASH_TABLE "peers" static vedis *vedis_ptr; int nc_dal_print_array(char* cmd); void nc_dal_start(nc_opts *opts) { int rc; rc = vedis_open(&vedis_ptr, ":mem:"); if(rc != VEDIS_OK) { nc_utils_die("nc:dal:start:open"); } nc_log_writef("info", "Data Access Layer has been started."); } int nc_dal_save_room(int room_code) { int rc; char time_str[NOW_STR_LEN]; nc_utils_now_str(time_str); rc = vedis_exec_fmt(vedis_ptr, "HSET %s '%d' '%s'", ROOMS_HASH_TABLE, room_code, time_str); if(rc != VEDIS_OK) { nc_utils_die("nc:dal:save:otoc"); } return 0; } int nc_dal_save_peer(char **record) { int rc; char time_str[NOW_STR_LEN]; nc_utils_now_str(time_str); rc = vedis_exec_fmt(vedis_ptr, "HSET %s '%s' '%s'", PEERS_HASH_TABLE, *record, time_str); if(rc != VEDIS_OK) { nc_utils_die("nc:dal:set:peer"); } return 0; } int nc_dal_print_peers() { char cmd[32] = "HKEYS "; strcat(cmd, PEERS_HASH_TABLE); if(nc_dal_print_array(cmd)) fprintf(stdout, "[-] No peers!\n"); return 0; } int nc_dal_print_rooms() { char cmd[32] = "HKEYS "; strcat(cmd, ROOMS_HASH_TABLE); if(nc_dal_print_array(cmd)) fprintf(stdout, "[-] No rooms!\n"); return 0; } int nc_dal_print_array(char *cmd) { int rc; vedis_value *result_ptr; rc = vedis_exec(vedis_ptr, cmd, -1); if(rc != VEDIS_OK) nc_utils_die("nc:dal:print:array:execute"); rc = vedis_exec_result(vedis_ptr, &result_ptr); if(rc != VEDIS_OK) nc_utils_die("nc:dal:print:array:result"); if(vedis_value_is_null(result_ptr)) { return 1; } else if(vedis_value_is_array(result_ptr)) { vedis_value *entry_ptr; while((entry_ptr = vedis_array_next_elem(result_ptr)) != 0) { if(!vedis_value_is_null(entry_ptr)) { const char *result_str = vedis_value_to_string(entry_ptr, 0); fprintf(stdout, "[+] %s\n", result_str); } } } else { const char *result_str; result_str = vedis_value_to_string(result_ptr, 0); fprintf(stdout, "[+] %s\n", result_str); return 0; } return 0; } ================================================ FILE: src/nc_disco.c ================================================ #include "nc.h" static void handle_request(int sock, struct sockaddr_in sock_remote_addr, char *body, int recv_str_len, char *remote_addr, int remote_port, nc_opts *opts); void* nc_disco_loop(void *opts) { int sock; struct sockaddr_in sock_local_addr; struct sockaddr_in sock_remote_addr; unsigned short sock_local_port; unsigned short sock_remote_port; socklen_t sock_remote_len = sizeof(sock_remote_addr); char recv_str[DCMD_BUF]; int recv_str_len; sock_local_port = atoi(DISCO_PORT); memset(&sock_local_addr, 0, sizeof(sock_local_addr)); sock_local_addr.sin_family = AF_INET; sock_local_addr.sin_addr.s_addr = htonl(INADDR_ANY); sock_local_addr.sin_port = htons(sock_local_port); if((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) nc_utils_die("nc:disco:loop:sock"); if(bind(sock, (struct sockaddr *) &sock_local_addr, sizeof(sock_local_addr)) < 0) nc_utils_die("nc:disco:loop:bind"); nc_log_writef("info", "Disco was started on port %s.", DISCO_PORT); for(;;) { if((recv_str_len = recvfrom(sock, recv_str, DCMD_BUF, 0, (struct sockaddr *) &sock_remote_addr, &sock_remote_len)) < 0) nc_utils_die("nc:disco:loop:recvfrom"); sock_remote_port = ntohs(sock_remote_addr.sin_port); recv_str[recv_str_len] = '\0'; /* do not handle requests initiated from myself */ if(strcmp(inet_ntoa(sock_remote_addr.sin_addr), ((nc_opts *) opts)->host) != 0) { nc_log_writef("info", "Discovery from %s:%d got: %s", inet_ntoa(sock_remote_addr.sin_addr), sock_remote_port, recv_str); handle_request(sock, sock_remote_addr, recv_str, recv_str_len, inet_ntoa(sock_remote_addr.sin_addr), sock_remote_port, opts); } } } int nc_disco_probe(nc_opts *opts) { return nc_udp_send(opts->broadcast, DISCO_PORT, DCMD_PROBE_REQUEST_CODE, 1); } void nc_disco_start(nc_opts *opts) { if(!opts->discoverable) return; nc_log_writef("info", "Discoverable mode has been started."); pthread_t disco_loop; pthread_create(&disco_loop, NULL, nc_disco_loop, opts); } void handle_request(int sock, struct sockaddr_in sock_remote_addr, char *body, int recv_str_len, char *remote_addr, int remote_port, nc_opts *opts) { char *body_payload; int response_len = DCMD_CODE_LEN+ PORT_MAX + HOST_MAX + USERNAME_MAX + HOSTNAME_MAX + 3; char response[response_len]; char username[USERNAME_MAX]; char hostname[HOSTNAME_MAX]; if(strncmp(body, DCMD_PROBE_REQUEST_CODE, DCMD_CODE_LEN) == 0) { /* --- request --- */ nc_names_get_hostname(hostname, HOSTNAME_MAX); nc_names_get_username(username, USERNAME_MAX); /* reply response to peer's DISCO_PORT with my IP and RPC PORT */ body_payload = body + DCMD_CODE_LEN; strcpy(response, DCMD_PROBE_RESPONSE_CODE); strcat(response, username); strcat(response, "@"); strcat(response, hostname); strcat(response, "/"); strcat(response, opts->host); strcat(response, ":"); strcat(response, opts->port); nc_log_writef("info", "Incoming discovery (UDP) request. code: %s, body: %s", DCMD_PROBE_REQUEST_CODE, body_payload); nc_udp_send(inet_ntoa(sock_remote_addr.sin_addr), DISCO_PORT, response, 0); /* reply ack to sock */ if(sendto(sock, DCMD_PROBE_ACK_CODE, DCMD_CODE_LEN, 0, (struct sockaddr *) &sock_remote_addr, sizeof(sock_remote_addr)) == -1) nc_utils_die("nc:disco:handler_request:sendto"); } else if(strncmp(body, DCMD_PROBE_RESPONSE_CODE, DCMD_CODE_LEN) == 0) { /* --- response --- */ /* receive RPC port as response */ body_payload = body + DCMD_CODE_LEN; nc_log_writef("info", "Incoming discovery (UDP) response. code: %s, body: %s", DCMD_PROBE_RESPONSE_CODE, body_payload); /* store available peers */ nc_dal_save_peer(&body_payload); } else { /* unknown command */ nc_log_writef("info", "Incoming discovery (UDP) uknown command: %s", body); } } ================================================ FILE: src/nc_json.c ================================================ #include "nc.h" int nc_json_make_otoc_msg(char **type, char **body, int original_body_len, char **msg) { JSON_Value *root_value = json_value_init_object(); JSON_Object *root_object = json_value_get_object(root_value); json_object_set_string(root_object, "type", *type); json_object_set_string(root_object, "body", *body); json_object_set_number(root_object, "original_body_len", original_body_len); *msg = json_serialize_to_string(root_value); return 0; } int nc_json_extract_otoc_msg(char **msg, char **type, int *original_body_len, char **body) { JSON_Value *root_value; JSON_Object *root_object; root_value = json_parse_string(*msg); root_object = json_value_get_object(root_value); *type = json_object_get_string(root_object, "type"); *body = json_object_get_string(root_object, "body"); *original_body_len = json_object_get_number(root_object, "original_body_len"); return 0; } ================================================ FILE: src/nc_log.c ================================================ #include "nc.h" static FILE *log_fp; pthread_mutex_t log_lock; void nc_log_start() { nc_conf_rec conf_rec = {"log_file"}; nc_conf_get(&conf_rec); log_fp = fopen(conf_rec.val, "a"); pthread_mutex_init(&log_lock, NULL); } void nc_log_write(const char *tag, const char *msg) { time_t now; time(&now); pthread_mutex_lock(&log_lock); fprintf(log_fp, "%s[%s]: %s\n", ctime(&now), tag, msg); fflush(log_fp); pthread_mutex_unlock(&log_lock); } void nc_do_log_writef(const char *tag, const char *msgf, va_list vargs) { pthread_mutex_lock(&log_lock); vfprintf(log_fp, msgf, vargs); fprintf(log_fp, "\n"); fflush(log_fp); va_end(vargs); pthread_mutex_unlock(&log_lock); } void nc_log_writef(const char *tag, const char *msgf, ...) { va_list vargs; int new_msgf_size; char time_str[NOW_STR_LEN]; nc_utils_now_str(time_str); new_msgf_size = strlen(time_str) + strlen(tag) + strlen(msgf) + 6; char new_msgf[new_msgf_size]; strcpy(new_msgf, time_str); strcat(new_msgf, " ["); strcat(new_msgf, tag); strcat(new_msgf, "] "); strcat(new_msgf, msgf); va_start(vargs, msgf); nc_do_log_writef(tag, new_msgf, vargs); } ================================================ FILE: src/nc_msg.c ================================================ #include "nc.h" void nc_msg_encode(int head_len, int body_len, char *head, char *body, char **buf) { *buf = (char*) malloc(head_len + body_len + 3); sprintf(*buf, "%d %s %d %s", head_len, head, body_len, body); } void nc_msg_free(char *buf) { free(buf); } ================================================ FILE: src/nc_names.c ================================================ #include "nc.h" int nc_names_get_hostname(char *buf, int len) { gethostname(buf, len); return 0; } int nc_names_get_username(char *buf, int len) { struct passwd *pw; uid_t uid; uid = geteuid(); pw = getpwuid(uid); if(pw) { strcpy(buf, pw->pw_name); return 0; } return 1; } ================================================ FILE: src/nc_netif.c ================================================ #include "nc.h" int nc_netif_get_addrs(char *inet, char *broadcast) { struct ifaddrs *ifap, *ifa; struct sockaddr_in *sa; struct sockaddr_in *bc; getifaddrs(&ifap); for(ifa = ifap; ifa; ifa = ifa->ifa_next) { if(ifa->ifa_addr->sa_family == AF_INET) { sa = (struct sockaddr_in *) ifa->ifa_addr; bc = (struct sockaddr_in *) ifa->ifa_broadaddr; /* ifa->ifa_dstaddr; */ if((strncmp(ifa->ifa_name, "en0", 3) == 0) || (strncmp(ifa->ifa_name, "en1", 4) == 0) || (strncmp(ifa->ifa_name, "em0", 3) == 0) || (strncmp(ifa->ifa_name, "eth0", 4) == 0)) { strcpy(inet, inet_ntoa(sa->sin_addr)); strcpy(broadcast, inet_ntoa(bc->sin_addr)); freeifaddrs(ifap); return 0; } } } freeifaddrs(ifap); return 1; } ================================================ FILE: src/nc_otoc.c ================================================ #include "nc.h" static nc_otoc_cmd cmds[OCMD_MAX]; static int cmd_current_code = 0; static int func_cmd_help(char *cmd); static int func_cmd_leave(char *cmd); void nc_otoc_start(nc_opts *opts, int pair_raw_sock) { int shell_fd_sock; int pair_fd_sock; fd_set readfds; int maxfds; char time_str[NOW_STR_LEN]; enum {init, none, text_sent, text_received, pkey_sent, pkey_received} last_action = init; shell_fd_sock = fileno(stdin); pair_fd_sock = nc_utils_get_rec_sockfd(pair_raw_sock); maxfds = pair_fd_sock + 1; /* --- start of sending public key --- */ if(opts->secure) { char *msg = NULL; char *msg_type = OTOC_MTYPE_PKEY; char *msg_body = NULL; /* --- start of encoding public key --- */ const char *plain_pkey = (const char*) my_publickey; int plain_pkey_len = crypto_box_PUBLICKEYBYTES; char encoded_pkey[Base64encode_len(plain_pkey_len)]; Base64encode(encoded_pkey, plain_pkey, plain_pkey_len); nc_log_writef("debug", "encode my public key: %s", encoded_pkey); /* --- end of encoding public key --- */ msg_body = encoded_pkey; nc_json_make_otoc_msg(&msg_type, &msg_body, plain_pkey_len, &msg); nn_send(pair_raw_sock, msg, strlen(msg), 0); nc_log_writef("debug", "one to one chat sent public key: %s", msg); last_action = pkey_sent; } /* --- end of sending public key --- */ /* --- start of shel command registration --- */ nc_otoc_register_cmd("/help", func_cmd_help); nc_otoc_register_cmd("/leave", func_cmd_leave); /* --- end of shell commmand registration --- */ for(;;) { switch(last_action) { case init: case pkey_sent: fprintf(stdout, ">> Entering (room code %d) ...\n", pair_raw_sock); fprintf(stdout, ">>> "); fflush(stdout); break; case text_received: fprintf(stdout, ">>> "); fflush(stdout); break; case text_sent: fprintf(stdout, ">>> "); fflush(stdout); break; case none: fprintf(stdout, ">>> "); fflush(stdout); break; case pkey_received: break; } FD_ZERO(&readfds); FD_SET(pair_fd_sock, &readfds); FD_SET(shell_fd_sock, &readfds); select(maxfds, &readfds, NULL, NULL, NULL); if(FD_ISSET(shell_fd_sock, &readfds)) { char *buf = NULL; size_t buf_sz = 1024; int i; nc_utils_now_str(time_str); getline(&buf, &buf_sz, stdin); if(buf[0] == '\n') { last_action = none; } else if(buf[0] == '/') { nc_utils_del_new_line(buf); for(i = 0; i < cmd_current_code; i++) { if(strstr(buf, cmds[i].name)) { if(cmds[i].func(buf) < 0) { return; } break; } } last_action = none; } else { if(opts->secure) { /* --- make ciphermsg with key pairs --- */ int ciphermsg_len = crypto_box_MACBYTES + strlen(buf); unsigned char nonce[crypto_box_NONCEBYTES]; unsigned char ciphermsg[ciphermsg_len]; /* @TODO: use random nonce */ //randombytes_buf(nonce, sizeof nonce); memset(nonce, '\0', sizeof nonce); crypto_box_easy(ciphermsg, (const unsigned char*) buf, strlen(buf), nonce, peers_publickey[pair_raw_sock], my_secretkey); /* --- make msg encoded with base64 --- */ const char *plain_ciphermsg = (const char*) ciphermsg; int plain_ciphermsg_len = ciphermsg_len; int encoded_ciphermsg_len = Base64encode_len(plain_ciphermsg_len); char encoded_ciphermsg[encoded_ciphermsg_len]; Base64encode(encoded_ciphermsg, plain_ciphermsg, plain_ciphermsg_len); nc_log_writef("debug", "encode ciphermsg: %s", encoded_ciphermsg); /* --- serialize msg with json --- */ char *msg = NULL; char *msg_type = OTOC_MTYPE_STXT; char *msg_body = NULL; msg_body = encoded_ciphermsg; nc_json_make_otoc_msg(&msg_type, &msg_body, strlen(buf), &msg); nc_log_writef("debug", "serialize encoded ciphermsg: %s", msg); /* --- send msg --- */ nn_send(pair_raw_sock, msg, strlen(msg), 0); } else { char *msg = NULL; char *msg_type = OTOC_MTYPE_RTXT; nc_json_make_otoc_msg(&msg_type, &buf, strlen(buf), &msg); nn_send(pair_raw_sock, msg, strlen(msg), 0); fprintf(stdout, "[%s] >>> %s", time_str, buf); fflush(stdout); nc_utils_del_new_line(buf); nc_log_writef("debug", "one to one chat sent: %s", buf); nc_utils_empty_string(buf); last_action = text_sent; } } } else if(FD_ISSET(pair_fd_sock, &readfds)) { char *buf = NULL; char *msg_body = NULL; char *msg_type = NULL; int original_msg_body_len; nn_recv(pair_raw_sock, &buf, NN_MSG, 0); nc_json_extract_otoc_msg(&buf, &msg_type, &original_msg_body_len, &msg_body); nc_utils_del_new_line(buf); nc_log_writef("debug", "one to one chat received: %s", msg_type); nn_freemsg(buf); if(strncmp(msg_type, OTOC_MTYPE_PKEY, OTOC_MTYPE_LEN) == 0) { /* public key message */ /* --- start of decoding public key --- */ int plain_pkey_len = Base64decode_len(msg_body); char plain_pkey[plain_pkey_len]; Base64decode(plain_pkey, (const char*) msg_body); strncpy(peers_publickey[pair_raw_sock], plain_pkey, crypto_box_PUBLICKEYBYTES); nc_log_writef("debug", "decoded peer's public key was stored."); /* --- end of decoding public key --- */ last_action = pkey_received; } else if(strncmp(msg_type, OTOC_MTYPE_RTXT, OTOC_MTYPE_LEN) == 0) { /* raw text message */ nc_utils_now_str(time_str); fprintf(stdout, "\r[%s] <<< %s", time_str, msg_body); fflush(stdout); last_action = text_received; } else if(strncmp(msg_type, OTOC_MTYPE_STXT, OTOC_MTYPE_LEN) == 0) { /* secure text message */ if(!opts->secure) { char *alert = "\r[%s] <<< { ... encrypted message ... }\n" "==========================================\n" "Your peer uses NanoChat undre secure mode.\n" "Use -s flag to see his encrypted messages.\n" "==========================================\n"; nc_utils_now_str(time_str); fprintf(stdout, alert, time_str); fflush(stdout); last_action = text_received; } else { /* --- decode secure msg body --- */ int plain_ciphermsg_len = Base64decode_len(msg_body); char plain_ciphermsg[plain_ciphermsg_len]; Base64decode(plain_ciphermsg, (const char*) msg_body); /* --- decrypt ciphermsg --- */ int decrypted_len = original_msg_body_len; int ciphermsg_len = crypto_box_MACBYTES + decrypted_len; unsigned char decrypted[decrypted_len]; unsigned char nonce[crypto_box_NONCEBYTES]; memset(nonce, '\0', sizeof nonce); crypto_box_open_easy(decrypted, plain_ciphermsg, ciphermsg_len, nonce, peers_publickey[pair_raw_sock], my_secretkey); /* --- show decrypted msg body --- */ decrypted[original_msg_body_len] = '\0'; nc_utils_now_str(time_str); fprintf(stdout, "\r[%s] <<< %s", time_str, decrypted); fflush(stdout); last_action = text_received; } } } } } void nc_otoc_register_cmd(char *cmd_name, int (*func)(char *cmd)) { cmds[cmd_current_code].code = cmd_current_code; cmds[cmd_current_code].func = func; strcpy(cmds[cmd_current_code].name, cmd_name); cmd_current_code++; } int func_cmd_help(char *cmd) { printf("Available commands:\n" " /help prints this text\n" " /leave leave current room\n" ); return 0; } int func_cmd_leave(char *cmd) { printf("leaving room ...\n"); return -1; } ================================================ FILE: src/nc_param.c ================================================ #include "nc.h" void nc_param_get_opts(nc_opts *opts, int argc, char **argv) { int c; int rc; opts->host[0] = '\0'; opts->broadcast[0] = '\0'; opts->port[0] = '\0'; opts->url[0] = '\0'; opts->verbose = 0; opts->secure = 0; opts->discoverable = 0; for(;;) { static struct option long_options[] = { {"verbose", no_argument, 0, 'v'}, {"secure", no_argument, 0, 's'}, {"discoverable", no_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {"host", required_argument, 0, 'H'}, {"broadcast", required_argument, 0, 'B'}, {"port", required_argument, 0, 'P'}, {0, 0, 0, 0} }; int option_index = 0; c = getopt_long(argc, argv, "vsdhH:P:", long_options, &option_index); if(c == -1 || c == 0) break; switch(c) { case 'v': opts->verbose = 1; break; case 's': opts->secure = 1; break; case 'd': opts->discoverable = 1; break; case 'h': nc_utils_print_help(); exit(0); case 'H': if(strlen(optarg) >= HOST_MAX) { fprintf(stderr, "Error: Host is to long!\n"); exit(1); } strcpy(opts->host, optarg); break; case 'B': if(strlen(optarg) >= HOST_MAX) { fprintf(stderr, "Error: Broadcast is to long!\n"); exit(1); } strcpy(opts->broadcast, optarg); break; case 'P': if(strlen(optarg) >= PORT_MAX) { fprintf(stderr, "Error: Port can not be more than 5 character!\n"); exit(1); } strcpy(opts->port, optarg); break; case '?': default: break; } } if(!opts->discoverable) { if(opts->host[0] == '\0') { rc = nc_netif_get_addrs(opts->host, opts->broadcast); if(rc != 0) { fprintf(stderr, "Error: no network interface was found!\n"); exit(0); } } } else { if(opts->host[0] == '\0' || opts->broadcast[0] == '\0') { rc = nc_netif_get_addrs(opts->host, opts->broadcast); if(rc != 0) { fprintf(stderr, "Error: no network interface was found!\n"); exit(0); } } } if(opts->port[0] == '\0') { strcpy(opts->port, DEFAULT_RPC_PORT); } nc_utils_make_url(opts->url, opts->host, opts->port); } ================================================ FILE: src/nc_rpc.c ================================================ #include "nc.h" void* nc_rpc_loop(void *void_opts) { nc_opts *opts = (nc_opts*) void_opts; int sock = nn_socket(AF_SP, NN_REP); nn_bind(sock, opts->url); nc_log_writef("info", "RPC has been started on URL: %s.", opts->url); for(;;) { char *buf = NULL; int bytes = nn_recv(sock, &buf, NN_MSG, 0); int cmp_rc; nc_log_writef("info", "Incoming RPC request: %s.", buf); if(strncmp(buf, RCMD_OTOC, RCMD_LEN) == 0) { /* one to one chat */ char otoc_url[URL_MAX]; static int otoc_port_init = INIT_OTOC_PORT; char otoc_port[OTOC_PORT_LEN]; int otoc_sock = nn_socket(AF_SP, NN_PAIR); snprintf(otoc_port, OTOC_PORT_LEN, "%d", otoc_port_init++); nc_utils_make_url(otoc_url, opts->host, otoc_port); nn_bind(otoc_sock, otoc_url); nc_log_writef("info", "New oto chat was connected on URL: %s.", otoc_url); nn_send(sock, otoc_port, OTOC_PORT_LEN, 0); nc_dal_save_room(otoc_sock); fprintf(stdout, "\r[*] You have new room. type '/list rooms'.\n>> "); fflush(stdout); } else { /* unknown command */ nn_send(sock, buf, bytes, 0); } nc_utils_empty_string(buf); nn_freemsg(buf); } return NULL; } void nc_rpc_start(nc_opts *opts) { pthread_t rpc_loop; pthread_create(&rpc_loop, NULL, nc_rpc_loop, opts); } ================================================ FILE: src/nc_shell.c ================================================ #include "nc.h" static nc_shell_cmd cmds[SCMD_MAX]; static int cmd_current_code = 0; static int current_room_sock; static int func_cmd_help(char *cmd, nc_opts *opts); static int func_cmd_ping(char *cmd, nc_opts *opts); static int func_cmd_quit(char *cmd, nc_opts *opts); static int func_cmd_connect(char *cmd, nc_opts *opts); static int func_cmd_attach(char *cmd, nc_opts *opts); static int func_cmd_probe(char *cmd, nc_opts *opts); static int func_cmd_list(char *cmd, nc_opts *otps); void nc_shell_start(nc_opts *opts) { fprintf(stdout, "NanoChat shell was started.\n"); /* --- start of shell command registeration --- */ nc_shell_register_cmd("/help", func_cmd_help); nc_shell_register_cmd("/ping", func_cmd_ping); nc_shell_register_cmd("/quit", func_cmd_quit); nc_shell_register_cmd("/connect", func_cmd_connect); nc_shell_register_cmd("/attach", func_cmd_attach); nc_shell_register_cmd("/probe", func_cmd_probe); nc_shell_register_cmd("/list", func_cmd_list); /* --- end of shell command registration --- */ for(;;) { char *buf = NULL; int i; int found; found = 0; buf = readline(">> "); if(buf[0] == 0) { found = 1; } else { nc_utils_del_new_line(buf); add_history(buf); for(i = 0; i < cmd_current_code; i++) { if(strstr(buf, cmds[i].name)) { if(cmds[i].func(buf, opts) < 0) { return; } found = 1; break; } } } if(!found) printf("Command not found!\n" "Type '/help' to list available commands.\n"); } } void nc_shell_register_cmd(char *cmd_name, int (*func)(char *cmd, nc_opts *opts)) { cmds[cmd_current_code].code = cmd_current_code; cmds[cmd_current_code].func = func; strcpy(cmds[cmd_current_code].name, cmd_name); cmd_current_code++; } int func_cmd_help(char *cmd, nc_opts *otps) { printf("Available commands:\n" " /help prints this text\n" " /probe find online peers\n" " /list peers list online peers\n" " /list rooms list availabe rooms\n" " /connect {host} {port} connect to remote client\n" " /attach {room} attach to room\n" " /quit quit nanochat console\n" ); return 0; } int func_cmd_ping(char *cmd, nc_opts *opts) { printf("pong\n"); return 0; } int func_cmd_quit(char *cmd, nc_opts *opts) { printf("Bye!\n"); return -1; } int func_cmd_attach(char *cmd, nc_opts *opts) { int pair_raw_sock; int rc; rc = sscanf(cmd, "/attach %d", &pair_raw_sock); if(rc != 1) { printf("Error: correct format is 'attach room'.\n"); } nc_log_writef("info", "Attached to room code: %d", pair_raw_sock); current_room_sock = pair_raw_sock; nc_otoc_start(opts, pair_raw_sock); return 0; } int func_cmd_connect(char *cmd, nc_opts *opts) { char host_req[HOST_MAX]; char port_req[PORT_MAX]; char url_req[URL_MAX]; char host_pair[HOST_MAX]; char port_pair[PORT_MAX]; char url_pair[URL_MAX]; int rc; char *buf = NULL; int sock_req; int sock_pair; rc = sscanf(cmd, "/connect %s %s", host_req, port_req); if(rc != 2) { fprintf(stdout, "Error: correct format is 'connect host port'.\n"); return 1; } nc_utils_make_url(url_req, host_req, port_req); nc_log_writef("info", "One to one chat request to %s was issued.", url_req); sock_req = nn_socket(AF_SP, NN_REQ); nn_connect(sock_req, url_req); nn_send(sock_req, RCMD_OTOC, RCMD_LEN, 0); nn_recv(sock_req, &buf, NN_MSG, 0); nn_shutdown(sock_req, 0); strcpy(host_pair, host_req); strncpy(port_pair, buf, OTOC_PORT_LEN); nn_freemsg(buf); nc_utils_make_url(url_pair, host_pair, port_pair); nc_log_writef("info", "One to one chat request was handshaked in %s.", url_pair); sock_pair = nn_socket(AF_SP, NN_PAIR); nn_connect(sock_pair, url_pair); current_room_sock = sock_pair; nc_otoc_start(opts, sock_pair); return 0; } int func_cmd_probe(char *cmd, nc_opts *opts) { int rc; int i; nc_log_writef("info", "User requested probe command."); rc = nc_disco_probe(opts); fprintf(stdout, "Starting to probing "); fflush(stdout); for(i = 0; i < DCMD_PROBE_TIMEOUT_SEC; i++) { sleep(1); fprintf(stdout, "."); fflush(stdout); } fprintf(stdout, "\n"); if(rc > 0) fprintf(stdout, "Ops, cannot send probe request!\n"); fprintf(stdout, "Done. Type '/list peers' to see available peers.\n"); return 0; } int func_cmd_list(char *cmd, nc_opts *opts) { char list_arg[32]; int rc; rc = sscanf(cmd, "/list %s", list_arg); if(rc != 1) { fprintf(stdout, "Error: correct format is 'list peers / rooms'.\n"); return 1; } if(strcmp(list_arg, "peers") == 0) { nc_dal_print_peers(); return 0; } else if(strcmp(list_arg, "rooms") == 0) { nc_dal_print_rooms(); return 0; } else { fprintf(stdout, "Error: correct format is 'list peers / rooms'.\n"); return 1; } } ================================================ FILE: src/nc_udp.c ================================================ #include "nc.h" int nc_udp_send(char *ip, char *port, char *body, int broadcast) { int sock; struct sockaddr_in broadcast_addr; int broadcast_permission; char *send_str = body; unsigned int send_str_len; if((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) return 1; if(broadcast == 1) { broadcast_permission = 1; if(setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (void *) &broadcast_permission, sizeof(broadcast_permission)) < 0) return 2; } memset(&broadcast_addr, 0, sizeof(broadcast_addr)); broadcast_addr.sin_family = AF_INET; broadcast_addr.sin_addr.s_addr = inet_addr(ip); broadcast_addr.sin_port = htons(atoi(port)); send_str_len = strlen(send_str); if(sendto(sock, send_str, send_str_len, 0, (struct sockaddr *) &broadcast_addr, sizeof(broadcast_addr)) != send_str_len) return 3; return 0; } ================================================ FILE: src/nc_utils.c ================================================ #include "nc.h" void nc_utils_print_help() { fprintf(stdout, "NanoChat help\n" "-------------\n" "Avaliable flags:\n" " Help: -h --help\n" " Host IP: -H --host {host IP}\n" " Broadcast IP: -B --broadcast {broadcast IP}\n" " RPC Port: -P --port {port}\n" " Discoverable: -d --discoverable\n" " Verbose: -v --verbose\n" " Secure: -s --secure\n" ); } int nc_utils_count_char(const char *str, const char chr) { char *chr_ptr; int count; count = 0; chr_ptr = strchr(str, chr); while(chr_ptr != NULL) { ++count; chr_ptr = strchr(chr_ptr + 1, chr); }; return count; } void nc_utils_now_str(char *time_str) { time_t now; time(&now); strftime(time_str, NOW_STR_LEN, "%Y-%m-%d %H:%M:%S", localtime(&now)); } void nc_utils_del_new_line(char *str) { char *new_line; if((new_line = strchr(str, '\n')) != NULL) { *new_line = '\0'; } } int nc_utils_has_new_line(char *str) { char *new_line = NULL; if((new_line = strchr(str, '\n')) == NULL) { return 0; } return 1; } void nc_utils_make_url(char *url, char *host, char *port) { url[0] = '\0'; strcat(url, "tcp://"); strcat(url, host); strcat(url, ":"); strcat(url, port); } int nc_utils_get_rec_sockfd(int sock) { int rcvfd; size_t rcvfd_sz = sizeof(rcvfd); nn_getsockopt(sock, NN_SOL_SOCKET, NN_RCVFD, &rcvfd, &rcvfd_sz); return rcvfd; } void nc_utils_empty_string(char *str) { memset(str, '\0', strlen(str)); } void nc_utils_die(char *str) { perror(str); exit(1); }