diff options
Diffstat (limited to 'PICA_client.c')
| -rwxr-xr-x | PICA_client.c | 1381 |
1 files changed, 1381 insertions, 0 deletions
diff --git a/PICA_client.c b/PICA_client.c new file mode 100755 index 0000000..8ac5f6b --- /dev/null +++ b/PICA_client.c @@ -0,0 +1,1381 @@ +#include "PICA_client.h" +#include "PICA_proto.h" +#include "PICA_msgproc.h" +#include "PICA_common.h" + +//#ifdef NO_RAND_DEV +//#include "PICA_rand_seed.h" +//#endif + +#define PICA_DEBUG // debug + +#ifdef PICA_DEBUG +#include <stdio.h> +#endif + +//static SSL_CTX* ctx; +//unsigned char __hellomsg[4]={0xCA,0xCA,PICA_PROTO_VER_HIGH,PICA_PROTO_VER_LOW}; + +//char _msgbuf[65536];// + +static struct PICA_client_callbacks callbacks; + +#ifdef PICA_MULTITHREADED + +#ifdef WIN32 +static HANDLE *mt_locks; +#else +static pthread_mutex_t *mt_locks; +#endif + +#endif +static unsigned int procmsg_INITRESP(unsigned char*,unsigned int,void*); +static unsigned int procmsg_CONNREQINC(unsigned char*,unsigned int,void*); +static unsigned int procmsg_NOTFOUND(unsigned char*,unsigned int,void*); +static unsigned int procmsg_FOUND(unsigned char*,unsigned int,void*); +static unsigned int procmsg_CLNODELIST(unsigned char*,unsigned int,void*); +static unsigned int procmsg_MSGUTF8(unsigned char*,unsigned int,void*); +static unsigned int procmsg_MSGOK(unsigned char*,unsigned int,void*); + +static struct PICA_proto_msg* c2n_writebuf_push(struct PICA_conninfo *ci, unsigned int msgid, unsigned int size); +static struct PICA_proto_msg* c2c_writebuf_push(struct PICA_chaninfo *chn, unsigned int msgid, unsigned int size); + +#define MSGINFO_MSGSNUM(arr) (sizeof(arr)/sizeof(struct PICA_msginfo)) +const struct PICA_msginfo c2n_messages[] = { + {PICA_PROTO_CONNREQINC, PICA_MSG_FIXED_SIZE, PICA_PROTO_CONNREQINC_SIZE, procmsg_CONNREQINC}, + {PICA_PROTO_NOTFOUND, PICA_MSG_FIXED_SIZE, PICA_PROTO_NOTFOUND_SIZE, procmsg_NOTFOUND}, + {PICA_PROTO_FOUND, PICA_MSG_FIXED_SIZE, PICA_PROTO_FOUND_SIZE, procmsg_FOUND}, + {PICA_PROTO_CLNODELIST, PICA_MSG_VAR_SIZE, PICA_MSG_VARSIZE_INT16, procmsg_CLNODELIST} +};//---!!! PING!!! + +const struct PICA_msginfo c2c_messages[] = { + {PICA_PROTO_MSGUTF8, PICA_MSG_VAR_SIZE, PICA_MSG_VARSIZE_INT16, procmsg_MSGUTF8}, + {PICA_PROTO_MSGOK, PICA_MSG_FIXED_SIZE, PICA_PROTO_MSGOK_SIZE, procmsg_MSGOK} +}; + +struct PICA_msginfo c2n_init_messages[] = { + {PICA_PROTO_INITRESPOK, PICA_MSG_FIXED_SIZE, PICA_PROTO_INITRESPOK_SIZE, procmsg_INITRESP}, + {PICA_PROTO_VERDIFFER, PICA_MSG_FIXED_SIZE, PICA_PROTO_VERDIFFER_SIZE, procmsg_INITRESP} +}; + + +// static int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata) +//{ +//strncpy(buf, (char *)(userdata), size); +//buf[size - 1] = 0; +//return(strlen(buf)); +//} + +//функция возвращает номер клиента id в бинарном виде, извлекая его из строки, +//которая возвращается функцией X509_NAME_oneline и представляет собой DN из сертификата клиента + static int get_id_fromsubjstr(char* DN_str,unsigned int* id) +{ +char* tmp1; +char* tmp2; + +tmp1=strstr(DN_str,"/CN="); + +if (!tmp1) + return 0; + +tmp2=strchr(tmp1,'#'); + +if (!tmp2) + return 0; + +*tmp2=0; + +tmp1+=4; + +if (tmp1==tmp2) + return 0; + +*id=(unsigned int)strtol(tmp1,0,10); + +*tmp2='#'; +return 1; +} + + int PICA_get_id_from_cert(const char *cert_file, unsigned int *p_id) + { + char *DN_str; + X509 *x; + FILE *f; + + f=fopen(cert_file,"r"); + + if (!f) + { + perror(cert_file); + return 0; + } + + x=PEM_read_X509(f,0,0,0); + fclose(f); + + DN_str=X509_NAME_oneline(X509_get_subject_name(x), 0, 0); + + if (!get_id_fromsubjstr(DN_str,p_id)) + { + OPENSSL_free(DN_str); + return 0; + } + printf("my id is %u\n",*p_id);//debug + puts(DN_str);//debug + + OPENSSL_free(DN_str); + return 1; + } + + + +static int add_chaninfo(struct PICA_conninfo *ci,struct PICA_chaninfo **chn,unsigned int uid,int is_outgoing) +{ +struct PICA_chaninfo *chnl,*ipt; + +chnl=*chn=(struct PICA_chaninfo*)calloc(sizeof(struct PICA_chaninfo),1); + +if (!chnl) + return 0;//ERR_CHECK + +chnl->conn=ci; +chnl->outgoing=is_outgoing; +chnl->peer_id=uid; + +chnl->sck_data = -1;//prevent closing stdin when closing not active connection + +chnl->read_buf = calloc(PICA_CHANREADBUFSIZE, 1); + +if (ci->chan_list_head) + { + ci->chan_list_end->next=chnl; + chnl->prev=ci->chan_list_end; + ci->chan_list_end=chnl; + } +else + { + ci->chan_list_head=chnl; + ci->chan_list_end=chnl; + } + +return 1; +} + +static int establish_data_connection(struct PICA_chaninfo *chnl) +{ +char* DN_str; +//unsigned char buf[12]; +int ret,err_ret; +unsigned int tmp_uid; +struct PICA_proto_msg *mp; + +chnl->sck_data=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + +{ +struct sockaddr_in addr=chnl->conn->srv_addr; + +addr.sin_port = htons(ntohs(addr.sin_port)); + +ret=connect( chnl->sck_data, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)); + +if (ret==SOCKET_ERROR) + { + err_ret=PICA_ERRSOCK; + goto error_ret; + } +} + +mp = c2c_writebuf_push( chnl, PICA_PROTO_CONNID, PICA_PROTO_CONNID_SIZE); +if (mp) + { + if (chnl->outgoing) + { + *(unsigned int*)(mp->tail) = chnl->conn->id;//вызвывающий + *(unsigned int*)(mp->tail + 4) = chnl->peer_id;//вызываемый + } + else + { + puts("callee CONNID");//debug + *(unsigned int*)(mp->tail) = chnl->peer_id;//вызвывающий + *(unsigned int*)(mp->tail + 4) = chnl->conn->id;//вызываемый + } + } +else + { + err_ret=PICA_ERRNOMEM; + goto error_ret; + } +//ret=send(chnl->sck_data,buf,PICA_PROTO_CONNID_SIZE,0); +do + { + err_ret = PICA_write_c2c(chnl); + if (PICA_OK != err_ret) + goto error_ret; + } +while( chnl->write_pos > 0); + +chnl->ssl=SSL_new(chnl->conn->ctx); + +if (!chnl->ssl) + { + puts("SSL_new !- 0");//debug + err_ret=PICA_ERRSSL; + goto error_ret; + } + +ret=SSL_set_fd(chnl->ssl, chnl->sck_data); + +if (!ret) + { + puts("SSL_set_fd returned 0");//debug + err_ret=PICA_ERRSSL; + goto error_ret_; + } + +SSL_set_verify(chnl->ssl, SSL_VERIFY_PEER,0); + +if (chnl->outgoing) +ret=SSL_connect(chnl->ssl); +else +ret=SSL_accept(chnl->ssl); + +printf("verify result:%i\n",(int)SSL_get_verify_result(chnl->ssl));//debug + +if (ret!=1) + { + printf("ret=%i of SSL_connect or accept\n",ret);//debug + printf("SSL_get_error says:%i\n",SSL_get_error(chnl->ssl,ret));//debug + err_ret=PICA_ERRSSL; + goto error_ret_; + } + +//проверить сертификат собеседника + +chnl->peer_cert=SSL_get_peer_certificate(chnl->ssl); + +if (!chnl->peer_cert) + { + err_ret=PICA_ERRNOPEERCERT; + goto error_ret_; + } + +DN_str=X509_NAME_oneline(X509_get_subject_name(chnl->peer_cert), 0, 0); + +if (!DN_str) + { + puts("no DN_str");//debug + err_ret=PICA_ERRSSL; + goto error_ret_; + } + +if (!get_id_fromsubjstr(DN_str,&tmp_uid) || tmp_uid!=chnl->peer_id) + { + err_ret=PICA_ERRINVPEERCERT; + goto error_ret_; + } +puts(DN_str);//debug + +OPENSSL_free(DN_str); + +{ +BIO *mem = BIO_new(BIO_s_mem()); +char *cert_pem; +unsigned int cert_pem_nb; + +PEM_write_bio_X509(mem, chnl->peer_cert); + +cert_pem_nb = (unsigned int)BIO_get_mem_data(mem, &cert_pem); + +if (callbacks.peer_cert_verify_cb(chnl->peer_id, (const char*)cert_pem, cert_pem_nb) == 0) + { + BIO_free(mem); + err_ret=PICA_ERRINVPEERCERT; + goto error_ret_; + } + +BIO_free(mem); +} + +chnl->state=PICA_CHANSTATE_ACTIVE; + +return PICA_OK; + +error_ret_: +error_ret: +callbacks.channel_failed(chnl->peer_id); +PICA_close_channel(chnl); +return err_ret; +} + +static unsigned int procmsg_INITRESP(unsigned char* buf, unsigned int nb,void* p) +{ +struct PICA_conninfo *ci=(struct PICA_conninfo *)p; + +switch(buf[0]) + { + case PICA_PROTO_INITRESPOK: + ci -> init_resp_ok = 1; + break; + + case PICA_PROTO_VERDIFFER: + ci->init_resp_ok = -1; + ci->node_ver_major = buf[2]; + ci->node_ver_minor = buf[3]; + break; + + default: + return 0; + } + +return 1; +} + +static unsigned int procmsg_CONNREQINC(unsigned char* buf,unsigned int nb,void* p) +{ +int ret; +struct PICA_conninfo *ci=(struct PICA_conninfo *)p; +struct PICA_proto_msg *mp; + + + if (callbacks.accept_cb(*(unsigned int*)(buf+2))) + { + struct PICA_chaninfo *_chnl; + + mp = c2n_writebuf_push( ci, PICA_PROTO_CONNALLOW, PICA_PROTO_CONNALLOW_SIZE); + + if (mp) + { + *(unsigned int*)(mp->tail) = *(unsigned int*)(buf+2); + } + else + return 0; + + ret = add_chaninfo( ci, &_chnl, *(unsigned int*)(buf+2), 0); + + if (!ret) + return 0; //ERR_CHECK - кончилась память + + do + { + ret = PICA_write_c2n(ci); + if (PICA_OK != ret) + return 0; + } + while( ci->write_pos > 0); + + ret = establish_data_connection(_chnl); + + if (ret!=PICA_OK) + return 1; + + callbacks.channel_established_cb(_chnl->peer_id);//*nchn=_chnl; + + return 1; + } + else + { + mp = c2n_writebuf_push( ci, PICA_PROTO_CONNDENY, PICA_PROTO_CONNDENY_SIZE); + + if (mp) + { + *(unsigned int*)(mp->tail) = *(unsigned int*)(buf+2); + } + else + return 0;//ERR_CHECK + } +return 1; +} + +static unsigned int procmsg_NOTFOUND(unsigned char* buf,unsigned int nb,void* p) +{ +struct PICA_conninfo *ci=(struct PICA_conninfo *)p; +struct PICA_chaninfo *ipt,*rq=0; + +puts("procmsg_NOTFOUND");//debug + +ipt=ci->chan_list_end; +while(ipt) + { + if (ipt->peer_id==*(unsigned int*)(buf+2)) + { + rq=ipt; + break; + } + ipt=ipt->prev; + } + +if (!rq) + return 0;// ERR_CHECK -левое сообщение, такой запрос не посылался + +puts("procmsg_NOTFOUND_chkp0");//debug +PICA_close_channel(rq); + +puts("procmsg_NOTFOUND_chkp1");//debug + +callbacks.notfound_cb(*(unsigned int*)(buf+2)); +return 1; +} + +static unsigned int procmsg_FOUND(unsigned char* buf,unsigned int nb,void* p) +{ +struct PICA_conninfo *ci=(struct PICA_conninfo *)p; +struct PICA_chaninfo *ipt,*rq=0; +int ret; + +ipt=ci->chan_list_end; +while(ipt) + { + if (ipt->peer_id==*(unsigned int*)(buf+2)) + { + rq=ipt; + break; + } + ipt=ipt->prev; + } + +if (!rq) + return 0;// ERR_CHECK -левое сообщение, такой запрос не посылался + +ret=establish_data_connection(rq); + +if (ret!=PICA_OK) + return 0; + +callbacks.channel_established_cb(rq->peer_id);//*nchn=_chnl; + +return 1; +} + +static unsigned int procmsg_CLNODELIST(unsigned char* buf, unsigned int nb, void* p) +{ +unsigned int pos = 4; +char ipaddr_string[INET6_ADDRSTRLEN]; +char temp_zeroswap; +unsigned int port; + +puts("CLNODELIST");//debug + +//callbacks.nodelist_cb(buf + 4, nb - 4); +do + { + switch(buf[pos]) + { + case PICA_PROTO_NEWNODE_IPV4: + + callbacks.nodelist_cb( + PICA_PROTO_NEWNODE_IPV4, + buf + pos +1, + inet_ntop(AF_INET, buf + pos +1, ipaddr_string, INET6_ADDRSTRLEN), + ntohs(*(uint16_t*)(buf + pos + 5)) + ); + + pos +=7; + break; + + case PICA_PROTO_NEWNODE_IPV6: + + callbacks.nodelist_cb( + PICA_PROTO_NEWNODE_IPV6, + buf + pos +1, + inet_ntop(AF_INET6, buf + pos +1, ipaddr_string, INET6_ADDRSTRLEN), + ntohs(*(uint16_t*)(buf + pos + 17)) + ); + + pos += 19; + break; + + case PICA_PROTO_NEWNODE_DNS: + + port = ntohs(*(uint16_t*)(buf + pos + buf[pos+1] + 2)); + + temp_zeroswap = buf[pos + 2 + buf[pos+1]]; //save byte after hostname string + buf[pos + 2 + buf[pos+1]] = '\0';// and replace it with string terminating zero + + callbacks.nodelist_cb( + PICA_PROTO_NEWNODE_DNS, + buf + pos +2, + buf + pos +2, + port + ); + + buf[pos + 2 + buf[pos+1]] = temp_zeroswap; + + pos += 4 + buf[pos+1]; + break; + + default: + puts("unknown node address type");//debug + return 0; + } + } +while(pos < nb); + +return 1; +} + +static unsigned int procmsg_MSGUTF8(unsigned char* buf, unsigned int nb, void* p) +{ +struct PICA_chaninfo *chan = (struct PICA_chaninfo *)p; +struct PICA_proto_msg *mp; + +callbacks.newmsg_cb(chan->peer_id, buf + 4, nb - 4, buf[0]); + +//send PICA_PROTO_MSGOK +mp = c2c_writebuf_push( chan, PICA_PROTO_MSGOK, PICA_PROTO_MSGOK_SIZE); + +if (mp) + { + RAND_bytes( mp->tail, 2); + } +else + return 0; + +return 1; +} + +static unsigned int procmsg_MSGOK(unsigned char* buf, unsigned int nb, void* p) +{ +struct PICA_chaninfo *chan = (struct PICA_chaninfo *)p; + +callbacks.msgok_cb(chan -> peer_id); +return 1; +} + + +#ifdef PICA_MULTITHREADED + +#ifdef WIN32 +void locking_cb(int mode, int type, char *file, int line) +{ + if (mode & CRYPTO_LOCK) + WaitForSingleObject(mt_locks[type],INFINITE); + else + ReleaseMutex(mt_locks[type]); +} +#else +unsigned long thread_id_cb(void) +{ + return (unsigned long)pthread_self(); +} + +void locking_cb(int mode, int type, char *file, int line) +{ + if (mode & CRYPTO_LOCK) + pthread_mutex_lock(&mt_locks[type]); + else + pthread_mutex_unlock(&mt_locks[type]); +} +#endif + +#endif + + +int PICA_client_init(struct PICA_client_callbacks *clcbs) +{ +struct sockaddr_in sd; + +if (!clcbs) + return 0; + +callbacks=*clcbs; + +SSL_load_error_strings(); +SSL_library_init(); + +#ifdef WIN32 +WSADATA wsd; +WSAStartup(MAKEWORD(2,2),&wsd); +#endif + +#ifdef NO_RAND_DEV +PICA_rand_seed(); +#endif + +#ifdef PICA_MULTITHREADED +{ + int i; +#ifdef WIN32 +mt_locks = malloc(CRYPTO_num_locks()*sizeof(HANDLE)); + +for (i=0; i<CRYPTO_num_locks(); i++) + mt_locks[i] = CreateMutex(NULL,FALSE,NULL); + +CRYPTO_set_locking_callback(locking_cb); +#else +mt_locks = malloc(CRYPTO_num_locks()*sizeof(pthread_mutex_t)); + +memset(mt_locks,0,CRYPTO_num_locks()*sizeof(pthread_mutex_t)); + +for (i=0; i<CRYPTO_num_locks(); i++) + pthread_mutex_init(&mt_locks[i],NULL); + +CRYPTO_set_id_callback(thread_id_cb); +CRYPTO_set_locking_callback(locking_cb); +#endif +} +#endif + +//ctx=SSL_CTX_new(TLSv1_method()); +//if (!ctx) +//;//ERR_CHECK + +return 1; +} + +/*адрес сервера, сертификат клиента и закрытый ключ*/ + +//Осуществляет подключение к серверу по указанному адресу. +//Аргументы: +//nodeaddr - адрес узла +//port - номер порта, в нативном для хост-машины порядке байт +//CA_file - имя файла, содержащего корневой сертификат в формате PEM +//cert_file - имя файла, содержащего сертфикат клиента в формате PEM +//pkey_file - имя файла, содержащего приватный ключ в формате PEM +//password_cb - callback-функция, запрашивающая пароль к приватному ключу, см. man 3 SSL_CTX_set_default_passwd_cb;в userdata передается указатель на id +//ci -указатель на указатель на структуру PICA_conninfo, который будет проинициализирован при успешном выполнении функции +//Память, выделенная под структуру, освобождается при вызове PICA_close_connection +//Возвращаемое значение - код завершения +//PICA_OK - успешное завершение функции +//.... + +int PICA_new_connection(const char *nodeaddr, + unsigned int port, + const char *CA_file, + const char *cert_file, + const char *pkey_file, + int (*password_cb)(char *buf, int size, int rwflag, void *userdata), + struct PICA_conninfo **ci) +{ +int ret,ret_err; +struct PICA_conninfo *cid; +struct sockaddr_in a; + +if (!(nodeaddr && CA_file && cert_file && pkey_file && ci)) + return PICA_ERRINVARG; + + +cid=*ci=(struct PICA_conninfo*)calloc(sizeof(struct PICA_conninfo),1);//(1) +if (!cid) + return PICA_ERRNOMEM; + +cid->read_buf = calloc(PICA_CONNREADBUFSIZE, 1);//(2) +if (!cid->read_buf) + { + ret_err = PICA_ERRNOMEM; + goto error_ret_1; + } + +cid->ctx=SSL_CTX_new(TLSv1_method());//(3) + +if (!cid->ctx) + { + ret_err=PICA_ERRSSL; + goto error_ret_2; + } + +if (!PICA_get_id_from_cert(cert_file, &cid->id)) + { + ret_err=PICA_ERRINVCERT; + goto error_ret_3; + } +////<< +if (password_cb) + { + SSL_CTX_set_default_passwd_cb(cid->ctx, password_cb); + SSL_CTX_set_default_passwd_cb_userdata(cid->ctx,&cid->id); + } + +ret=SSL_CTX_use_certificate_file(cid->ctx,cert_file,SSL_FILETYPE_PEM); +if (ret!=1) + {//ERR_CHECK + ret_err=PICA_ERRSSL; + goto error_ret_3; + } +ret=SSL_CTX_use_PrivateKey_file(cid->ctx,pkey_file,SSL_FILETYPE_PEM); +if (ret!=1) + {//ERR_CHECK + ret_err=PICA_ERRSSL; + goto error_ret_3; + } +////<< + +ret=SSL_CTX_load_verify_locations(cid->ctx,CA_file,0/*"trustedCA/"*/); +printf("loadverifylocations ret=%i\n",ret);//debug +SSL_CTX_set_client_CA_list(cid->ctx,SSL_load_client_CA_file(CA_file)); + + +cid->ssl_comm=SSL_new(cid->ctx);//(4) + +if (!cid->ssl_comm) + { + ret_err=PICA_ERRSSL; + goto error_ret_3; + } + + +//resolving name +{ + struct addrinfo h,*r; + +#ifdef AI_IDN +#define H_AI_FLAGS (AI_ADDRCONFIG | AI_IDN) +#else +#define H_AI_FLAGS AI_ADDRCONFIG +#endif + + memset(&h,0,sizeof(struct addrinfo)); + + h.ai_flags=H_AI_FLAGS; + h.ai_family=AF_INET;//CONF + h.ai_socktype=SOCK_STREAM; + h.ai_protocol=IPPROTO_TCP; + + if (0!=getaddrinfo(nodeaddr,NULL,&h,&r)) + { + ret_err=PICA_ERRDNSRESOLVE; + goto error_ret_4; + } + + memset(&a,0,sizeof(a)); + memcpy(&a,r->ai_addr,r->ai_addrlen); + a.sin_family = AF_INET; + //a.sin_addr.s_addr = inet_addr (nodeaddr); + a.sin_port = htons(port); + + freeaddrinfo(r); +} + +cid->srv_addr=a; + +cid->sck_comm=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//(5) +if (cid->sck_comm==SOCKET_ERROR) + { + ret_err=PICA_ERRSOCK; + goto error_ret_4; + } + +cid->state = PICA_CONNSTATE_CONNECTING; + +ret=connect(cid->sck_comm,(struct sockaddr*)&a,sizeof(struct sockaddr_in)); + + +if (ret==SOCKET_ERROR) + { + ret_err=PICA_ERRSOCK; + goto error_ret_5; + } + + +{ +struct PICA_proto_msg *mp; + +mp = c2n_writebuf_push( cid, PICA_PROTO_INITREQ, PICA_PROTO_INITREQ_SIZE); + +if (mp) + { + mp->tail[0] = PICA_PROTO_VER_HIGH; + mp->tail[1] = PICA_PROTO_VER_LOW; + } +else + { + ret_err = PICA_ERRNOMEM; + goto error_ret_5; + } +} + +do + { + ret = PICA_write_c2n(cid); + if (ret != PICA_OK) + { + ret_err = ret; + break; + } + ret = PICA_read_c2n(cid); + if (ret != PICA_OK) + { + ret_err = ret; + break; + } + //TODO Timeout + } +while (cid->init_resp_ok == 0); + +if (cid->init_resp_ok != 1) + { + if (cid->init_resp_ok == -1) + ret_err = PICA_ERRVERDIFFER; + + goto error_ret_5; + } + +printf("6\n");//debug + +ret=SSL_set_fd(cid->ssl_comm,cid->sck_comm); + +printf("SSL_set_fd=%i\n",ret); + +ret=SSL_accept(cid->ssl_comm); + +if (ret!=1) + { + printf("SSL_accept ret=%i\n SSL_get_error=%i\n",ret,SSL_get_error(cid->ssl_comm,ret));//debug + //ERR_CHECK + ret_err=PICA_ERRSSL; + goto error_ret_5; + } +cid->state = PICA_CONNSTATE_CONNECTED;//<<<!!! + +{ +struct PICA_proto_msg *mp; + +mp = c2n_writebuf_push( cid, PICA_PROTO_CLNODELISTREQ, PICA_PROTO_CLNODELISTREQ_SIZE); + +if (mp) + { + RAND_bytes(mp->tail, 2); + } +} + +return PICA_OK; + + +error_ret_5: //(5) + SHUTDOWN(cid->sck_comm); + CLOSE(cid->sck_comm); + +error_ret_4: //(4) + SSL_free(cid->ssl_comm); + +error_ret_3: //(3) + SSL_CTX_free(cid->ctx); + +error_ret_2: //(2) + free(cid->read_buf); + +error_ret_1: //(1) + free(cid); + *ci=0; + +return ret_err; +} + + + +//создает ИСХОДЯЩИЙ логический зашифрованный канал связи с указанным собеседником, если тот доступен +// в данный момент. + +int PICA_create_channel(struct PICA_conninfo *ci,unsigned int uid,struct PICA_chaninfo **chn) +{ +struct PICA_proto_msg *mp; +struct PICA_chaninfo *chnl; +char* DN_str; +unsigned char buf[12]; +int ret,err_ret; + +if (!(ci && chn)) + return PICA_ERRINVARG; + + +if (!add_chaninfo(ci,chn,uid,1)) + return PICA_ERRNOMEM; + +chnl=*chn; + +mp = c2n_writebuf_push( ci, PICA_PROTO_CONNREQOUTG, PICA_PROTO_CONNREQOUTG_SIZE); + + if (mp) + { + *(unsigned int*)(mp->tail)=uid; + } + else + return PICA_ERRNOMEM; + + +return PICA_OK; + +//сделать нормальную обработку ошибок - со стековой последовательностью деинициализирующих процедур (!!??) +// в этой функции и в предыдущей + +} + +int PICA_read(struct PICA_conninfo *ci,int timeout) +{ +fd_set fds; +struct timeval tv={0,1000}; +struct PICA_chaninfo *ipt, *kill_ptr = 0; +int ret,nfds; + +//puts("PICA_read<<");//debug + +tv.tv_usec=timeout; + +FD_ZERO(&fds); + +nfds=ci->sck_comm; +FD_SET(ci->sck_comm,&fds); + +ipt=ci->chan_list_head; + +while(ipt) + { + if (ipt->state==PICA_CHANSTATE_ACTIVE) + { + FD_SET(ipt->sck_data,&fds); + + if (ipt->sck_data>nfds) + nfds=ipt->sck_data; + } + + ipt=ipt->next; + } + +#warning "make sockets non-blocking!!!!" + +ret=select(nfds+1,&fds,0,0,&tv); + +if (ret>0) + { + + if (FD_ISSET(ci->sck_comm,&fds)) + { + ret=PICA_read_c2n(ci); + if (ret!=PICA_OK) + return ret; + } + + ipt=ci->chan_list_head; + while(ipt) + { + if (FD_ISSET(ipt->sck_data, &fds)) + { + if (PICA_OK != (ret = PICA_read_c2c(ipt)) ) + { + callbacks.channel_closed_cb(ipt->peer_id, ret); + kill_ptr = ipt; + } + + } + + ipt=ipt->next; + + if (kill_ptr) + { + PICA_close_channel(kill_ptr); + kill_ptr = 0; + } + } + + } +//puts("PICA_read>>");//debug + +return PICA_OK; +} + +int PICA_write(struct PICA_conninfo *ci) +{ +struct PICA_chaninfo *ipt, *kill_ptr = 0; +int ret = PICA_OK; + +//puts("PICA_write>>");//debug + +if (ci->write_pos) + ret = PICA_write_c2n(ci); + +if (PICA_OK != ret) + return ret; + +ipt=ci->chan_list_head; + +while(ipt) + { + if (ipt->write_pos) + { + if (PICA_OK != (ret = PICA_write_c2c(ipt))) + { + callbacks.channel_closed_cb(ipt->peer_id, ret); + kill_ptr = ipt; + } + } + ipt = ipt->next; + + if (kill_ptr) + { + PICA_close_channel(kill_ptr); + kill_ptr = 0; + } + } + +//puts("PICA_write<<");//debug +return PICA_OK; +} + +int PICA_write_socket(SOCKET s, unsigned char *buf, unsigned int *ppos) +{ +int ret; + +if (*ppos > 0 ) + { + ret = send( s, buf, *ppos, MSG_NOSIGNAL); + + if (ret > 0) + { + if (ret < *ppos) + memmove( buf, buf + ret, *ppos - ret); + *ppos -= ret; + } + + if (ret < 0) + { +#ifdef WIN32 + ret = WSAGetLastError(); + if (! (ret == WSAEWOULDBLOCK || ret == WSAENOBUFS)) +#else + ret = errno; + if (! (ret == EAGAIN || ret == ENOBUFS || ret == EINTR)) +#endif + return PICA_ERRSOCK; + } + if (ret == 0) + return PICA_ERRSERV; + + } +return PICA_OK; +} + +int PICA_write_ssl(SSL *ssl, unsigned char *buf, unsigned int *ppos) +{ +int ret; + +if (*ppos > 0 ) + { + ret = SSL_write( ssl, buf, *ppos); + + if (ret > 0) + { + if (ret < *ppos) + memmove( buf, buf + ret, *ppos - ret); + *ppos -= ret; + } + + if (ret < 0) + { + ret = SSL_get_error( ssl, ret); + if (! (ret == SSL_ERROR_WANT_WRITE || ret == SSL_ERROR_WANT_READ)) + return PICA_ERRSSL; + } + if (ret == 0) + return PICA_ERRSERV; + + } +return PICA_OK; +} + +//int PICA_read_socket() +//int PICA_read_ssl() + +int PICA_write_c2n(struct PICA_conninfo *ci) +{ +int ret = PICA_ERRINVARG; + +if (PICA_CONNSTATE_CONNECTING == ci->state) + ret = PICA_write_socket( ci->sck_comm, ci->write_buf, &ci->write_pos); +else if (PICA_CONNSTATE_CONNECTED == ci->state) + ret = PICA_write_ssl( ci->ssl_comm, ci->write_buf, &ci->write_pos); + +return ret; +} + +int PICA_write_c2c(struct PICA_chaninfo *chn) +{ +int ret = PICA_ERRINVARG; + +if (PICA_CHANSTATE_ACTIVE != chn->state) + ret = PICA_write_socket( chn->sck_data, chn->write_buf, &chn->write_pos); +else + ret = PICA_write_ssl( chn->ssl, chn->write_buf, &chn->write_pos); + +return ret; +} + +int PICA_read_socket(SOCKET s, unsigned char *buf, unsigned int *ppos, unsigned int size) +{ +int ret; + +ret = recv(s, buf + *ppos, size - *ppos, 0); + +if (ret == 0) + return PICA_ERRDISCONNECT; + +if (ret < 0) + { +#ifdef WIN32 + switch(WSAGetLastError()) + { + case WSAEINTR: + case WSAEINPROGRESS: + case WSAEWOULDBLOCK: + break; + default: + return PICA_ERRSOCK; + } +#else + switch(errno) + { + case EAGAIN : +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + case EINTR: + break; + + default: + return PICA_ERRSOCK;//ERR_CHECK + } +#endif + } + +if (ret > 0) + { + *ppos += ret; + } + +return PICA_OK; +} + +int PICA_read_ssl(SSL *ssl, unsigned char *buf, unsigned int *ppos, unsigned int size) +{ +int ret; + +ret = SSL_read(ssl, buf + *ppos, size - *ppos); + +if (!ret) + return PICA_ERRSSL; + +if (ret < 0) + switch(SSL_get_error(ssl, ret)) + { + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_READ: + break; + default: + return PICA_ERRSSL; + }; + +if (ret > 0) + { + *ppos += ret; + } + +return PICA_OK; +} + +int PICA_read_c2c(struct PICA_chaninfo *chn) +{ +int ret; + +if (chn->state != PICA_CHANSTATE_ACTIVE) + return PICA_OK;// ??? + +//ret = SSL_read(chn->ssl , chn->read_buf + chn->read_pos, PICA_CHANREADBUFSIZE - chn->read_pos); +ret = PICA_read_ssl(chn->ssl, chn->read_buf, &chn->read_pos, PICA_CHANREADBUFSIZE); + +if (ret == PICA_OK) + { + if(!PICA_processdatastream(chn->read_buf, &(chn->read_pos ), chn, c2c_messages, MSGINFO_MSGSNUM(c2c_messages))) + return PICA_ERRSERV; + } + +return ret; +} + +//функция читает и обрабатывает данные, приходящие от сервера по управляющему соединению. +int PICA_read_c2n(struct PICA_conninfo *ci) +{ +int ret; + +//ret=SSL_read(ci->ssl_comm, ci->read_buf + ci->read_pos, PICA_CONNREADBUFSIZE - ci->read_pos); +if (PICA_CONNSTATE_CONNECTING == ci->state) + ret = PICA_read_socket(ci->sck_comm, ci->read_buf, &ci->read_pos, PICA_CONNREADBUFSIZE); +else if (PICA_CONNSTATE_CONNECTED == ci->state) + ret = PICA_read_ssl(ci->ssl_comm, ci->read_buf, &ci->read_pos, PICA_CONNREADBUFSIZE); + + + +if (ret == PICA_OK) + { + if (PICA_CONNSTATE_CONNECTING == ci->state) + if(!PICA_processdatastream( ci->read_buf, &(ci->read_pos), ci, c2n_init_messages, MSGINFO_MSGSNUM(c2n_init_messages))) + return PICA_ERRSERV; + + if (PICA_CONNSTATE_CONNECTED == ci->state) + if(!PICA_processdatastream( ci->read_buf, &(ci->read_pos), ci, c2n_messages, MSGINFO_MSGSNUM(c2n_messages))) + return PICA_ERRSERV; + } + +return ret; +} + +struct PICA_proto_msg* c2n_writebuf_push(struct PICA_conninfo *ci, unsigned int msgid, unsigned int size) +{ +struct PICA_proto_msg *mp; + +if (! ci->write_buf) + { + ci->write_buf = calloc(PICA_CONNWRITEBUFSIZE, 1); + if (!ci->write_buf) + return 0; + ci->write_buflen = PICA_CONNWRITEBUFSIZE; + } + +if (ci->write_buflen - ci->write_pos < size) + { + unsigned char*p; + p = realloc(ci->write_buf, size + ci->write_pos); + + if (p) + { + ci->write_buf = p; + ci->write_buflen = size + ci->write_pos; + } + else + return 0; + } + +mp = (struct PICA_proto_msg *)(ci->write_buf + ci->write_pos); +mp->head[0] = mp->head[1] = msgid; +ci->write_pos += size; + +return mp; +} + +struct PICA_proto_msg* c2c_writebuf_push(struct PICA_chaninfo *chn, unsigned int msgid, unsigned int size) +{ +struct PICA_proto_msg *mp; + +if (! chn->write_buf) + { + chn->write_buf = calloc(PICA_CHANWRITEBUFSIZE, 1); + if (!chn->write_buf) + return 0; + chn->write_buflen = PICA_CHANWRITEBUFSIZE; + } + +if (chn->write_buflen - chn->write_pos < size) + { + unsigned char*p; + p = realloc(chn->write_buf, size + chn->write_pos); + + if (p) + { + chn->write_buf = p; + chn->write_buflen = size + chn->write_pos; + } + else + return 0; + } + +mp = (struct PICA_proto_msg *)(chn->write_buf + chn->write_pos); +mp->head[0] = mp->head[1] = msgid; +chn->write_pos += size; + +return mp; +} + +int PICA_send_msg(struct PICA_chaninfo *chn, char *buf,unsigned int len) +{ +struct PICA_proto_msg *mp; + +if (len > PICA_PROTO_C2CMSG_MAXDATASIZE) + return PICA_ERRMSGSIZE; + +if ((mp = c2c_writebuf_push(chn, PICA_PROTO_MSGUTF8, len + 4))) + { + *((uint16_t*)mp->tail) = len; + memcpy(mp->tail + 2, buf, len); + } +else + return PICA_ERRNOMEM; + +return PICA_OK; +} + +void PICA_close_channel(struct PICA_chaninfo *chn) +{ +struct PICA_chaninfo *ipt; + +puts("PICA_close_channel");//debug + +ipt=chn->conn->chan_list_head; +puts("PICA_close_channel_chkp0");//debug +while(ipt) + { + if (ipt==chn) + { + if (ipt->next) + ipt->next->prev=ipt->prev; + else + chn->conn->chan_list_end=ipt->prev; + + if (ipt->prev) + ipt->prev->next=ipt->next; + else + chn->conn->chan_list_head=ipt->next; + + break; + } + ipt=ipt->next; + } + +puts("PICA_close_channel_chkp1");//debug + +if (chn -> read_buf) + free(chn -> read_buf); + +if (chn -> write_buf) + free(chn -> write_buf); + +if (chn->peer_cert) + X509_free(chn->peer_cert); + +puts("PICA_close_channel_chkp2");//debug + +if (chn->ssl) + SSL_shutdown(chn->ssl); + puts("PICA_close_channel_chkp3");//debug + +if (chn->ssl) + { + printf("3 SSL_free(%X)\n",chn->ssl);//debug + SSL_free(chn->ssl); + } + +puts("PICA_close_channel_chkp4");//debug +SHUTDOWN(chn->sck_data); +puts("PICA_close_channel_chkp5");//debug +CLOSE(chn->sck_data); +puts("PICA_close_channel_chkp6");//debug +free(chn); +puts("PICA_close_channel_chkp7");//debug +} + +void PICA_close_connection(struct PICA_conninfo *cid) +{ +struct PICA_chaninfo *ipt; + +while((ipt = cid->chan_list_head)) + PICA_close_channel(ipt); + +SSL_shutdown(cid->ssl_comm); +printf("4 SSL_free(%X)\n",cid->ssl_comm);//debug +SSL_free(cid->ssl_comm); +SHUTDOWN(cid->sck_comm); +CLOSE(cid->sck_comm); +SSL_CTX_free(cid->ctx); +free(cid->read_buf); + +if (cid->write_buf) + free(cid->write_buf); + +free(cid); +} |
