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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <time.h>
#include <sys/select.h>
#include <stdarg.h>
#include <pthread.h>
#include <pwd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <nanomsg/nn.h>
#include <nanomsg/reqrep.h>
#include <nanomsg/pair.h>
#include <vedis.h>
#include <sodium.h>
#include <parson.h>
#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);
}
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
SYMBOL INDEX (63 symbols across 18 files)
FILE: src/nc.c
function main (line 3) | int
FILE: src/nc.h
type nc_opts (line 85) | typedef struct nc_opts {
type nc_conf_rec (line 95) | typedef struct nc_conf_rec {
type nc_shell_cmd (line 100) | typedef struct nc_shell_cmd {
type nc_otoc_cmd (line 106) | typedef struct nc_otoc_cmd {
type nc_array (line 112) | typedef struct nc_array {
FILE: src/nc_array.c
function nc_array (line 3) | nc_array*
function nc_array_string_set (line 21) | int
function nc_array_string_len (line 34) | int
function nc_array_string_free (line 40) | int
FILE: src/nc_conf.c
function nc_conf_start (line 5) | void
function nc_conf_get (line 11) | void
FILE: src/nc_crypto.c
function nc_crypto_start (line 9) | void
FILE: src/nc_dal.c
function nc_dal_start (line 10) | void
function nc_dal_save_room (line 23) | int
function nc_dal_save_peer (line 41) | int
function nc_dal_print_peers (line 59) | int
function nc_dal_print_rooms (line 70) | int
function nc_dal_print_array (line 80) | int
FILE: src/nc_disco.c
type sockaddr_in (line 4) | struct sockaddr_in
type sockaddr_in (line 13) | struct sockaddr_in
type sockaddr_in (line 14) | struct sockaddr_in
type sockaddr (line 32) | struct sockaddr
type sockaddr (line 40) | struct sockaddr
function nc_disco_probe (line 64) | int
function nc_disco_start (line 71) | void
function handle_request (line 83) | void
FILE: src/nc_json.c
function nc_json_make_otoc_msg (line 3) | int
function nc_json_extract_otoc_msg (line 16) | int
FILE: src/nc_log.c
function nc_log_start (line 6) | void
function nc_log_write (line 15) | void
function nc_do_log_writef (line 27) | void
function nc_log_writef (line 38) | void
FILE: src/nc_msg.c
function nc_msg_encode (line 3) | void
function nc_msg_free (line 10) | void
FILE: src/nc_names.c
function nc_names_get_hostname (line 3) | int
function nc_names_get_username (line 10) | int
FILE: src/nc_netif.c
function nc_netif_get_addrs (line 3) | int
FILE: src/nc_otoc.c
function nc_otoc_start (line 9) | void
function nc_otoc_register_cmd (line 267) | void
function func_cmd_help (line 277) | int
function func_cmd_leave (line 287) | int
FILE: src/nc_param.c
function nc_param_get_opts (line 3) | void
FILE: src/nc_rpc.c
function nc_rpc_start (line 52) | void
FILE: src/nc_shell.c
function nc_shell_start (line 15) | void
function nc_shell_register_cmd (line 70) | void
function func_cmd_help (line 80) | int
function func_cmd_ping (line 96) | int
function func_cmd_quit (line 103) | int
function func_cmd_attach (line 110) | int
function func_cmd_connect (line 130) | int
function func_cmd_probe (line 180) | int
function func_cmd_list (line 206) | int
FILE: src/nc_udp.c
function nc_udp_send (line 3) | int
FILE: src/nc_utils.c
function nc_utils_print_help (line 3) | void
function nc_utils_count_char (line 20) | int
function nc_utils_now_str (line 37) | void
function nc_utils_del_new_line (line 47) | void
function nc_utils_has_new_line (line 57) | int
function nc_utils_make_url (line 69) | void
function nc_utils_get_rec_sockfd (line 79) | int
function nc_utils_empty_string (line 88) | void
function nc_utils_die (line 94) | void
Condensed preview — 34 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (51K chars).
[
{
"path": ".gitignore",
"chars": 320,
"preview": ".emacs\nnanochat\nvar\n\n/autom4te.cache/\n/config/\nMakefile\nMakefile.in\naclocal.m4\napp.info\ncompile\nconfig.h\nconfig.h.in\ncon"
},
{
"path": "Makefile.am",
"chars": 506,
"preview": "SUBDIRS = lib/nanomsg lib/vedis lib/sodium lib/parson lib/base64 src\n\ninstall-data-hook:\n\t@echo bin dir $(bindir)\n\t@echo"
},
{
"path": "README.md",
"chars": 5127,
"preview": "NanoChat\n=====\n\nNanoChat is a **peer-to-peer**, **end-to-end encrypted** and **discoverable** chat application\nthat can "
},
{
"path": "TODO.md",
"chars": 309,
"preview": "TODO\n=====\n\n- peer rpc (req/rep protocol)\n- discovery (udp broadcasting)\n- one to one chat (pair protocol)\n- group chat "
},
{
"path": "configure.ac",
"chars": 1533,
"preview": "AC_PREREQ([2.69])\nAC_INIT([nanochat], [0.1], [hamidreza.s@gmail.com])\nAM_INIT_AUTOMAKE([foreign])\n\nNANOMSG_VERSION=0.8-b"
},
{
"path": "lib/base64/.gitignore",
"chars": 13,
"preview": "base64-1.0.0\n"
},
{
"path": "lib/base64/Makefile.am",
"chars": 438,
"preview": "all-local:\n\t@if [ ! -f ./base64-$(BASE64_VERSION)/lib/libbase64.a ]; then \\\n\t\ttar -zxf ./base64-$(BASE64_VERSION).tar.gz"
},
{
"path": "lib/nanomsg/.gitignore",
"chars": 17,
"preview": "nanomsg-0.8-beta\n"
},
{
"path": "lib/nanomsg/Makefile.am",
"chars": 381,
"preview": "all-local:\n\t@if [ ! -f ./nanomsg-$(NANOMSG_VERSION)/lib/libnanomsg.a ]; then \\\n\t\ttar -zxf ./nanomsg-$(NANOMSG_VERSION).t"
},
{
"path": "lib/parson/.gitignore",
"chars": 13,
"preview": "parson-1.0.0\n"
},
{
"path": "lib/parson/Makefile.am",
"chars": 438,
"preview": "all-local:\n\t@if [ ! -f ./parson-$(PARSON_VERSION)/lib/libparson.a ]; then \\\n\t\ttar -zxf ./parson-$(PARSON_VERSION).tar.gz"
},
{
"path": "lib/sodium/.gitignore",
"chars": 16,
"preview": "libsodium-1.0.9\n"
},
{
"path": "lib/sodium/Makefile.am",
"chars": 385,
"preview": "all-local:\n\t@if [ ! -f ./libsodium-$(SODIUM_VERSION)/lib/libsodium.a ]; then \\\n\t\ttar -zxf ./libsodium-$(SODIUM_VERSION)."
},
{
"path": "lib/vedis/.gitignore",
"chars": 12,
"preview": "vedis-1.2.6\n"
},
{
"path": "lib/vedis/Makefile.am",
"chars": 420,
"preview": "all-local:\n\t@if [ ! -f ./vedis-$(VEDIS_VERSION)/lib/libvedis.a ]; then \\\n\t\ttar -zxf ./vedis-$(VEDIS_VERSION).tar.gz && \\"
},
{
"path": "src/Makefile.am",
"chars": 1408,
"preview": "all-local:\n\tmkdir -p $(top_srcdir)/var\n\trm -f $(top_srcdir)/var/nanochat.conf\n\techo \"log_file=./var/nanochat.log\" >> $(t"
},
{
"path": "src/nc.c",
"chars": 344,
"preview": "#include \"nc.h\"\n\nint\nmain(int argc, char **argv)\n{\n nc_opts opts;\n\n /* @NOTE: conf and log must start first respective"
},
{
"path": "src/nc.h",
"chars": 4779,
"preview": "#ifndef NC_H\n#define NC_H\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <getopt.h>\n#include <stri"
},
{
"path": "src/nc_array.c",
"chars": 919,
"preview": "#include \"nc.h\"\n\nnc_array*\nnc_array_string_new(int array_len, int string_len)\n{\n int i;\n char **array_ptr = (char**) m"
},
{
"path": "src/nc_conf.c",
"chars": 504,
"preview": "#include \"nc.h\"\n\nstatic FILE *conf_fp;\n\nvoid\nnc_conf_start()\n{\n conf_fp = fopen(\"./var/nanochat.conf\", \"r\");\n}\n\nvoid\nnc"
},
{
"path": "src/nc_crypto.c",
"chars": 410,
"preview": "#include \"nc.h\"\n\nunsigned char my_publickey[crypto_box_PUBLICKEYBYTES];\nunsigned char my_secretkey[crypto_box_SECRETKEYB"
},
{
"path": "src/nc_dal.c",
"chars": 2154,
"preview": "#include \"nc.h\"\n\n#define ROOMS_HASH_TABLE \"otoc\"\n#define PEERS_HASH_TABLE \"peers\"\n\nstatic vedis *vedis_ptr;\n\nint nc_dal_"
},
{
"path": "src/nc_disco.c",
"chars": 4056,
"preview": "#include \"nc.h\"\n\nstatic void\nhandle_request(int sock, struct sockaddr_in sock_remote_addr,\n\t char *body, int recv_"
},
{
"path": "src/nc_json.c",
"chars": 919,
"preview": "#include \"nc.h\"\n\nint\nnc_json_make_otoc_msg(char **type, char **body, int original_body_len, char **msg)\n{\n JSON_Value *"
},
{
"path": "src/nc_log.c",
"chars": 1165,
"preview": "#include \"nc.h\"\n\nstatic FILE *log_fp;\npthread_mutex_t log_lock;\n\nvoid\nnc_log_start()\n{\n nc_conf_rec conf_rec = {\"log_fi"
},
{
"path": "src/nc_msg.c",
"chars": 264,
"preview": "#include \"nc.h\"\n\nvoid\nnc_msg_encode(int head_len, int body_len, char *head, char *body, char **buf)\n{\n *buf = (char*) m"
},
{
"path": "src/nc_names.c",
"chars": 303,
"preview": "#include \"nc.h\"\n\nint\nnc_names_get_hostname(char *buf, int len)\n{\n gethostname(buf, len);\n return 0;\n}\n\nint\nnc_names_ge"
},
{
"path": "src/nc_netif.c",
"chars": 768,
"preview": "#include \"nc.h\"\n\nint\nnc_netif_get_addrs(char *inet, char *broadcast)\n{\n struct ifaddrs *ifap, *ifa;\n struct sockaddr_i"
},
{
"path": "src/nc_otoc.c",
"chars": 7513,
"preview": "#include \"nc.h\"\n\nstatic nc_otoc_cmd cmds[OCMD_MAX];\nstatic int cmd_current_code = 0;\n\nstatic int func_cmd_help(char *cmd"
},
{
"path": "src/nc_param.c",
"chars": 2258,
"preview": "#include \"nc.h\"\n\nvoid\nnc_param_get_opts(nc_opts *opts, int argc, char **argv)\n{\n int c;\n int rc;\n \n opts->host[0] = "
},
{
"path": "src/nc_rpc.c",
"chars": 1367,
"preview": "#include \"nc.h\"\n\nvoid*\nnc_rpc_loop(void *void_opts)\n{\n nc_opts *opts = (nc_opts*) void_opts;\n int sock = nn_socket(AF_"
},
{
"path": "src/nc_shell.c",
"chars": 4995,
"preview": "#include \"nc.h\"\n\nstatic nc_shell_cmd cmds[SCMD_MAX];\nstatic int cmd_current_code = 0;\nstatic int current_room_sock;\n\nsta"
},
{
"path": "src/nc_udp.c",
"chars": 880,
"preview": "#include \"nc.h\"\n\nint\nnc_udp_send(char *ip, char *port, char *body, int broadcast)\n{\n int sock;\n struct sockaddr_in bro"
},
{
"path": "src/nc_utils.c",
"chars": 1594,
"preview": "#include \"nc.h\"\n\nvoid\nnc_utils_print_help()\n{\n fprintf(stdout,\n\t \"NanoChat help\\n\"\n\t \"-------------\\n\"\n\t \"Avaliable "
}
]
About this extraction
This page contains the full source code of the hamidreza-s/NanoChat GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 34 files (45.4 KB), approximately 14.2k tokens, and a symbol index with 63 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.