/* * Secure Network Transport Layer Library v2 implementation. * (sntllv2) it superseed all versions before due to the: * - memory consumption * - new features such as pulse emitting * - performance optimization * * This is a proprietary software. See COPYING for further details. * * (c) Askele Group 2013-2015 * */ #include #include #define __USE_GNU #include #include #include #include #ifdef WIN32 #include #include #include #else #include #include #include #include #include #include #include #include #include #endif #include #include #include #include #include // #include #include #include #include #define DEBUG #define FREE(x) { if (x) { free(x); x = NULL; } } #define MAX_STREAMS INT_MAX typedef struct __datastream_type { int dsid; usrtc_node_t node; // readdir_r DIR *dp; struct dirent dent; struct dirent *dres; } datastream_t; usrtc_t *_rd_streams; int _rd_last_id = 0; pthread_rwlock_t _lock; /* helper functions */ static long __cmp_int(const void *a, const void *b) { return *(int *)a - *(int *)b; } inline void dump_dirent(struct dirent *d, char *buf) { if (d == NULL) return; strcat(buf, "\""); strcat(buf, d->d_name); strcat(buf, "\" "); switch(d->d_type) { case DT_REG: strcat(buf, "\"regular\""); break; case DT_DIR: strcat(buf, "\"directory\""); break; case DT_BLK: strcat(buf, "\"block\""); break; case DT_CHR: strcat(buf, "\"char\""); break; case DT_FIFO: strcat(buf, "\"fifo\""); break; case DT_LNK: strcat(buf, "\"link\""); break; case DT_UNKNOWN: strcat(buf, "\"unknown\""); break; default: strcat(buf, "\"another\""); break; } } int __openlistener(int port) { int sd; struct sockaddr_in addr; sd = socket(PF_INET, SOCK_STREAM, 0); bzero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = INADDR_ANY; if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 ) { perror("can't bind port"); abort(); } if ( listen(sd, 10) != 0 ) { perror("Can't configure listening port"); abort(); } return sd; } /* * Validation of the SSL certificate * this function must be exist. */ static int __validate_sslpem(conn_t *co) { return 0; } /* * validate - authorize user with password */ static int __secure_check(conn_t *co) { return SNE_SUCCESS; } /* * typed list callback */ static int __set_typed_list_callback(conn_t *co, int ch, char *desc) { printf("allowed channel %d (%s)\n", ch, desc); return SNE_SUCCESS; } /* list of rpc calls functions */ usrtc_t *fulist; /* our fake */ usrtc_t *__rettlist(conn_t *c) { return fulist; } /* RPC functions implementation */ static int __dir_open(void *m, sexp_t *sx) { sexp_t *lsx = NULL; sxmsg_t *msg = (sxmsg_t *)m; char *buf; size_t ln = 0; #ifdef DEBUG char dbuf[2048]; print_sexp(dbuf, sizeof(dbuf), sx); printf("%s: got sx '%s'\n", __FUNCTION__, dbuf); #endif /* DEBUG */ if(sexp_list_cdr(sx, &lsx) || !sx->list->next || !sx->list->next->val) { printf("Invalid protocol\n"); return sxmsg_return(msg, SNE_BADPROTO); } char *dir_name = strdup(sx->list->next->val); if ( usrtc_isfull(_rd_streams) ) { return sxmsg_return(msg, SNE_TOOLONG); } datastream_t *item; if ( !(item = malloc(sizeof(datastream_t))) ) { return sxmsg_return(msg, SNE_ENOMEM); } /* find free id */ pthread_rwlock_wrlock(&_lock); do { if (_rd_last_id == INT_MAX) _rd_last_id = 1; else ++_rd_last_id; } while (usrtc_lookup(_rd_streams, &_rd_last_id)); /* create rd stream */ DIR *dp = NULL; if ( !(dp = opendir(dir_name)) ) { pthread_rwlock_unlock(&_lock); FREE(item); FREE(dir_name); return sxmsg_return(msg, SNE_FAILED); } FREE(dir_name); /* init stream */ item->dsid = _rd_last_id; item->dp = dp; item->dres = NULL; usrtc_node_init(&(item->node), item); usrtc_insert(_rd_streams, &(item->node), &item->dsid); buf = sxmsg_rapidbuf(msg); ln = snprintf(buf, MAX_RBBUF_LEN, "(dir-stream %d)", item->dsid); pthread_rwlock_unlock(&_lock); #ifdef DEBUG printf("%s: stream %d has been opened\n", __FUNCTION__, item->dsid); #endif /* DEBUG */ return sxmsg_rreply(msg, ln + 1); } static int __dir_read(void *m, sexp_t *sx) { sexp_t *lsx = NULL; sxmsg_t *msg = (sxmsg_t *)m; char *buf; size_t ln = 0; int stid = -1; #ifdef DEBUG char dbuf[2048]; print_sexp(dbuf, sizeof(dbuf), sx); printf("%s: got sx '%s'\n", __FUNCTION__, dbuf); #endif /* DEBUG */ if (sexp_list_cdr(sx, &lsx) || !sx->list->next || !sx->list->next->val || (0 >= (stid = atoi(sx->list->next->val) )) ) { printf("Invalid protocol\n"); return sxmsg_return(msg, SNE_BADPROTO); } /* get stream item */ datastream_t *item; usrtc_node_t *node; pthread_rwlock_rdlock(&_lock); if ( !(node = usrtc_lookup(_rd_streams, &stid)) ) { pthread_rwlock_unlock(&_lock); return sxmsg_return(msg, SNE_INVALINDEX); } pthread_rwlock_unlock(&_lock); pthread_rwlock_wrlock(&_lock); item = usrtc_node_getdata(node); if (readdir_r(item->dp, &(item->dent), &(item->dres))) { #ifdef DEBUG printf("%s: readdir_r() is failed (%d)\n", __FUNCTION__, errno); #endif /* DEBUG */ pthread_rwlock_unlock(&_lock); return sxmsg_return(msg, SNE_FAILED); } if (!item->dres) { buf = sxmsg_rapidbuf(msg); ln = snprintf(buf, MAX_RBBUF_LEN, "(dir-end %d)", item->dsid); goto __finish; } char dump[2048]; sprintf(dump, "(dir-entry ("); dump_dirent(item->dres, dump); strcat(dump, "))"); #ifdef DEBUG printf("%s: dump = '%s'\n", __FUNCTION__, dump); #endif /* DEBUG */ buf = sxmsg_rapidbuf(msg); ln = snprintf(buf, MAX_RBBUF_LEN, "%s", dump); __finish: pthread_rwlock_unlock(&_lock); return sxmsg_rreply(msg, ln + 1); } static int __dir_close(void *m, sexp_t *sx) { sexp_t *lsx = NULL; sxmsg_t *msg = (sxmsg_t *)m; int stid = -1; #ifdef DEBUG char dbuf[2048]; print_sexp(dbuf, sizeof(dbuf), sx); printf("%s: got sx '%s'\n", __FUNCTION__, dbuf); #endif /* DEBUG */ if (sexp_list_cdr(sx, &lsx) || !sx->list->next || !sx->list->next->val || (0 >= (stid = atoi(sx->list->next->val) )) ) { printf("Invalid protocol\n"); return sxmsg_return(msg, SNE_BADPROTO); } /* get stream item */ datastream_t *item; usrtc_node_t *node; pthread_rwlock_rdlock(&_lock); if ( !(node = usrtc_lookup(_rd_streams, &stid)) ) { pthread_rwlock_unlock(&_lock); return sxmsg_return(msg, ENOENT); } pthread_rwlock_unlock(&_lock); pthread_rwlock_wrlock(&_lock); item = usrtc_node_getdata(node); closedir(item->dp); usrtc_delete(_rd_streams, node); FREE(item); pthread_rwlock_unlock(&_lock); #ifdef DEBUG printf("%s: stream %d has been closed\n", __FUNCTION__, stid); #endif /* DEBUG */ return sxmsg_return(msg, SNE_SUCCESS); } /* define a little bit */ #define DEFAULT_PORT 13133 static void sigpipe_handler(int a) { return; } struct startbundle { int client; conn_sys_t *ssys; }; static void *__starter_thread(void *stb); int main(int argc, char **argv) { signal(SIGPIPE, SIG_IGN); char *rootca = NULL, *cert = NULL; conn_sys_t *ssys = connections_create(); int port = DEFAULT_PORT; int opt; while((opt = getopt(argc, argv, "p:r:u:")) != -1) { switch(opt) { case 'p': port = atoi(optarg); break; case 'r': rootca = strdup(optarg); break; case 'u': cert = strdup(optarg); break; default: fprintf(stderr, "usage: %s [-p ] -r -u \n", argv[0]); return EINVAL; } } if(!rootca) { fprintf(stderr, "Root CA not pointed.\n Failure.\n"); return EINVAL; } if(!cert) { fprintf(stderr, "User certificate not pointed.\n Failure.\n"); return EINVAL; } sntl_init(); /* all is fine let's init connection subsystem */ if(!ssys) { fprintf(stderr, "Subsystem init failed: %d\n", opt); return 2; } /* set wroking certificates */ opt = connections_setsslserts(ssys, rootca, cert, cert); if(opt) { fprintf(stderr, "Subsystem init failed (set SSL x.509 pems): %d\n", opt); return opt; } /* clean up */ free(rootca); free(cert); /* set important callbacks to do the security checking */ connections_set_authcheck(ssys, __secure_check); connections_set_sslvalidate(ssys, __validate_sslpem); /* set a callback, it's optional and doesn't required in server side apps */ connections_set_channelcall(ssys, __set_typed_list_callback); /* ok, now we need to construct RPC lists (channels) */ if(!(fulist = malloc(sizeof(usrtc_t)))) { fprintf(stderr, "Cannot allocate memory for RPC lists\n Failure.\n"); return ENOMEM; } opt = sntl_rpclist_init(fulist); if(opt) { fprintf(stderr, "Failed to init rpc list\n Failure.\n"); return opt; } /* we will add one channel with type id 12 "Demo rpc list" */ opt = sntl_rpclist_add(fulist, 12, "Demo RPC list", NULL); if(opt) { fprintf(stderr, "Failed to add typed RPC channel\n Failure.\n"); return opt; } opt = sntl_rpclist_add(fulist, 1, "Public", NULL); if(opt) { fprintf(stderr, "Failed to add typed RPC channel\n Failure.\n"); return opt; } /* ok, let's add stream functions */ opt = sntl_rpclist_add_function(fulist, 12, "dir-open", __dir_open); if(opt) { __fail: fprintf(stderr, "Failed to add functions to typed RPC channel\n Failure.\n"); return opt; } opt = sntl_rpclist_add_function(fulist, 12, "dir-read", __dir_read); if(opt) goto __fail; opt = sntl_rpclist_add_function(fulist, 12, "dir-close", __dir_close); if(opt) goto __fail; /* ok, setup it */ connections_set_rpcvalidator(ssys, __rettlist); /* create stream tree */ if(!(_rd_streams = malloc(sizeof(usrtc_t)) )) return ENOMEM; usrtc_init(_rd_streams, USRTC_REDBLACK, MAX_STREAMS, __cmp_int); /* now we're ready to run the listen process */ int srv = __openlistener(port); pthread_t starter; while(1) { struct sockaddr_in addr; socklen_t len = sizeof(addr); struct startbundle bundle; conn_t *co; bundle.ssys = ssys; int client = accept(srv, (struct sockaddr*)&addr, &len); /* accept connection as usual */ blub("."); co = connection_master_link(ssys, client, NULL); if(!co) { fprintf(stderr, "Cannot create connetion (%d)\n", errno); } #if 0 bundle.client = client; pthread_create(&starter, NULL, __starter_thread, &bundle); pthread_detach(starter); // pthread_join(starter, &co); //__starter_thread(&bundle); usleep(2000); #endif } connections_destroy(ssys); sntl_finalize(); return 0; } static void *__starter_thread(void *stb) { struct startbundle *s = (struct startbundle *)stb; int client = s->client; conn_sys_t *ssys = s->ssys; conn_t *co = connection_master_link(ssys, client, NULL); if(!co) { fprintf(stderr, "Cannot create connetion (%d)\n", errno); } return co; }