void ServerConnection::receive() { int res = tcp_recv_msg(conn.dia_conn, &conn.rb, 0, CONN_WAIT_USECS); if (res < 0) { if (res == AAA_CONN_SHUTDOWN) { INFO( M_NAME "receive(): shutdown - closing connection.\n"); closeConnection(true); } else { closeConnection(); ERROR( M_NAME "receive(): tcp_recv_reply() failed.\n"); } return; } if (!res) // nothing received return; /* obtain the structure corresponding to the message */ AAAMessage* msg = AAATranslateMessage(conn.rb.buf, conn.rb.buf_len, 0); if(!msg) { ERROR( M_NAME "receive(): message structure not obtained from message.\n"); closeConnection(); return; } #ifdef EXTRA_DEBUG AAAPrintMessage(msg); #endif if (is_req(msg)) handleRequest(msg); else handleReply(msg); AAAFreeMessage(&msg); }
/** * Receive Loop for Diameter messages. * Decodes the message and calls receive_message(). * @param sock - the socket to receive from * @returns when the socket is closed */ void receive_loop(int sock) { char buf[hdr_len],*msg; int buf_len,length,version,cnt,msg_len; AAAMessage *dmsg; while(!*shutdownx){ buf_len=0; while(buf_len<1){ cnt = select_recv(sock,buf+buf_len,1,0); if (cnt<0) goto error; buf_len+=cnt; } version = (unsigned char)buf[0]; if (version!=1) { LOG(L_ERR,"ERROR:receive_loop():[%d] Recv Unknown version [%d]\n",sock,(unsigned char)buf[0]); continue; } while(buf_len<hdr_len){ cnt = select_recv(sock,buf+buf_len,hdr_len-buf_len,0); if (cnt<0) goto error; buf_len+=cnt; } length = get_3bytes(buf+1); if (length>DP_MAX_MSG_LENGTH){ LOG(L_ERR,"ERROR:receive_loop():[%d] Msg too big [%d] bytes\n",sock,length); goto error; } LOG(L_DBG,"DBG:receive_loop():[%d] Recv Version %d Length %d\n",sock,version,length); msg = shm_malloc(length); if (!msg) { LOG_NO_MEM("shm",length); goto error; } memcpy(msg,buf,hdr_len); msg_len=hdr_len; while(msg_len<length){ cnt = select_recv(sock,msg+msg_len,length-msg_len,0); if (cnt<0) { shm_free(msg); goto error; } msg_len+=cnt; } LOG(L_DBG,"DBG:receive_loop():[%d] Recv message complete\n",sock); dmsg = AAATranslateMessage((unsigned char*)msg,(unsigned int)msg_len,1); /*shm_free(msg);*/ if (dmsg) receive_message(dmsg,sock); else{ shm_free(msg); } } error: if (this_peer) { if (this_peer->I_sock == sock) sm_process(this_peer,I_Peer_Disc,0,0,sock); if (this_peer->R_sock == sock) sm_process(this_peer,R_Peer_Disc,0,0,sock); } LOG(L_ERR,"INFO:receive_loop():[%d] Client closed connection or error... BYE\n",sock); }
/*! \brief send a message over an already opened TCP connection */ int tcp_send_recv(int sockfd, char* buf, int len, rd_buf_t* rb, unsigned int waited_id) { int n, number_of_tries; fd_set active_fd_set, read_fd_set; struct timeval tv; unsigned long int result_code; AAAMessage *msg; AAA_AVP *avp; char serviceType; unsigned int m_id; /* try to write the message to the Diameter client */ while( (n=write(sockfd, buf, len))==-1 ) { if (errno==EINTR) continue; LM_ERR("write returned error: %s\n", strerror(errno)); return AAA_ERROR; } if (n!=len) { LM_ERR("write gave no error but wrote less than asked\n"); return AAA_ERROR; } /* wait for the answer a limited amount of time */ tv.tv_sec = MAX_WAIT_SEC; tv.tv_usec = MAX_WAIT_USEC; /* Initialize the set of active sockets. */ FD_ZERO (&active_fd_set); FD_SET (sockfd, &active_fd_set); number_of_tries = 0; while(number_of_tries<MAX_TRIES) { read_fd_set = active_fd_set; if (select (sockfd+1, &read_fd_set, NULL, NULL, &tv) < 0) { LM_ERR("select function failed\n"); return AAA_ERROR; } /* if (!FD_ISSET (sockfd, &read_fd_set)) { LM_ERR("no response received\n"); // return AAA_ERROR; } */ /* Data arriving on a already-connected socket. */ reset_read_buffer(rb); switch( do_read(sockfd, rb) ) { case CONN_ERROR: LM_ERR("failed to read from socket\n"); return AAA_CONN_CLOSED; case CONN_CLOSED: LM_ERR("failed to read from socket\n"); return AAA_CONN_CLOSED; } /* obtain the structure corresponding to the message */ msg = AAATranslateMessage(rb->buf, rb->buf_len, 0); if(!msg) { LM_ERR("message structure not obtained\n"); return AAA_ERROR; } avp = AAAFindMatchingAVP(msg, NULL, AVP_SIP_MSGID, vendorID, AAA_FORWARD_SEARCH); if(!avp) { LM_ERR("AVP_SIP_MSGID not found\n"); return AAA_ERROR; } m_id = *((unsigned int*)(avp->data.s)); LM_DBG("######## m_id=%d\n", m_id); if(m_id!=waited_id) { number_of_tries ++; LM_NOTICE("old message received\n"); continue; } goto next; } LM_ERR("too many old messages received\n"); return AAA_TIMEOUT; next: /* Finally die correct answer */ avp = AAAFindMatchingAVP(msg, NULL, AVP_Service_Type, vendorID, AAA_FORWARD_SEARCH); if(!avp) { LM_ERR("AVP_Service_Type not found\n"); return AAA_ERROR; } serviceType = avp->data.s[0]; result_code = ntohl(*((unsigned long int*)(msg->res_code->data.s))); switch(result_code) { case AAA_SUCCESS: /* 2001 */ return ACC_SUCCESS; default: /* error */ return ACC_FAILURE; } }
void ServerConnection::openConnection() { DBG("init TCP connection\n"); if (conn.dia_conn) { ERROR("CRITICAL: trying to open new connection, while current one still" " opened.\n"); abort(); } conn.dia_conn = tcp_create_connection(server_name.c_str(), server_port, ca_file.c_str(), cert_file.c_str()); if (!conn.dia_conn) { ERROR("establishing connection to %s\n", server_name.c_str()); setRetryConnectLater(); return; } // send CER AAAMessage* cer; if ((cer=AAAInMessage(AAA_CC_CER, AAA_APP_DIAMETER_COMMON_MSG))==NULL) { ERROR(M_NAME":openConnection(): can't create new " "CER AAA message!\n"); conn.terminate(); setRetryConnectLater(); return; } if (addOrigin(cer) || addDataAVP(cer, AVP_Host_IP_Address, origin_ip_address, sizeof(origin_ip_address)) || addDataAVP(cer, AVP_Vendor_Id, (char*)&vendorID, sizeof(vendorID)) || addDataAVP(cer, AVP_Supported_Vendor_Id, (char*)&vendorID, sizeof(vendorID)) || addStringAVP(cer, AVP_Product_Name, product_name)) { ERROR("openConnection(): adding AVPs failed\n"); conn.terminate(); setRetryConnectLater(); return; } // supported applications AAA_AVP* vs_appid; if( (vs_appid=AAACreateAVP(AVP_Vendor_Specific_Application_Id, (AAA_AVPFlag)AAA_AVP_FLAG_NONE, 0, 0, 0, AVP_DONT_FREE_DATA)) == 0) { ERROR( M_NAME":openConnection(): creating AVP failed." " (no more free memory!)\n"); conn.terminate(); setRetryConnectLater(); return; } // feels like c coding... if (addGroupedAVP(vs_appid, AVP_Auth_Application_Id, (char*)&app_id, sizeof(app_id)) || addGroupedAVP(vs_appid, AVP_Vendor_Id, (char*)&vendorID, sizeof(vendorID)) || (AAAAddAVPToMessage(cer, vs_appid, 0) != AAA_ERR_SUCCESS) ) { ERROR( M_NAME":openConnection(): creating AVP failed." " (no more free memory!)\n"); conn.terminate(); setRetryConnectLater(); return; } #ifdef EXTRA_DEBUG AAAPrintMessage(cer); #endif conn.setIDs(cer); if(AAABuildMsgBuffer(cer) != AAA_ERR_SUCCESS) { ERROR( " openConnection(): message buffer not created\n"); AAAFreeMessage(&cer); return; } int ret = tcp_send(conn.dia_conn, cer->buf.s, cer->buf.len); if (ret) { ERROR( "openConnection(): could not send message\n"); conn.terminate(); setRetryConnectLater(); AAAFreeMessage(&cer); return; } AAAFreeMessage(&cer); unsigned int cea_receive_cnt = 3; while (true) { int res = tcp_recv_msg(conn.dia_conn, &conn.rb, CONNECT_CEA_REPLY_TIMEOUT, 0); if (res <= 0) { if (!res) { ERROR( " openConnection(): did not receive response (CEA).\n"); } else { ERROR( " openConnection(): error receiving response (CEA).\n"); } conn.terminate(); setRetryConnectLater(); return; } /* obtain the structure corresponding to the message */ AAAMessage* cea = AAATranslateMessage(conn.rb.buf, conn.rb.buf_len, 0); if(!cea) { ERROR( " openConnection(): could not decipher response (CEA).\n"); conn.terminate(); setRetryConnectLater(); return; } if (cea->commandCode == AAA_CC_CEA) { #ifdef EXTRA_DEBUG AAAPrintMessage(cea); #endif AAAFreeMessage(&cea); break; } AAAFreeMessage(&cea); if (!(cea_receive_cnt--)) { ERROR( " openConnection(): no CEA received.\n"); conn.terminate(); setRetryConnectLater(); return; } } DBG("Connection opened.\n"); open = true; }
/** * Does the actual receive operations on the Diameter TCP socket, for retrieving incoming messages. * The functions is to be called iteratively, each time there is something to be read from the TCP socket. It uses * a simple state machine to read first the version, then the header and then the rest of the message. When an * entire message is received, it is decoded and passed to the processing functions. * @param sp - the serviced peer to operate on * @return 1 on success, 0 on failure */ static inline int do_receive(serviced_peer_t *sp) { int cnt,n,version; char *dst; AAAMessage *dmsg; switch (sp->state){ case Receiver_Waiting: n = 1; /* wait for version */ dst = sp->buf; break; case Receiver_Header: n = DIAMETER_HEADER_LEN - sp->buf_len; /* waiting for rest of header */ dst = sp->buf+sp->buf_len; break; case Receiver_Rest_of_Message: n = sp->length - sp->msg_len; /* waiting for the rest of the message */ dst = sp->msg+sp->msg_len; break; default: LM_ERR("do_receive(): [%.*s] Unknown state %d\n", sp->p?sp->p->fqdn.len:0, sp->p?sp->p->fqdn.s:0, sp->state); goto error_and_reset; } cnt = recv(sp->tcp_socket,dst,n,0); if (cnt<=0) goto error_and_reset; switch (sp->state){ case Receiver_Waiting: version = (unsigned char)(sp->buf[0]); if (version!=1) { LM_ERR("do_receive(): [%.*s] Received Unknown version [%d]\n", sp->p->fqdn.len, sp->p->fqdn.s, (unsigned char)sp->buf[0]); goto error_and_reset; }else{ sp->state = Receiver_Header; sp->buf_len = 1; } break; case Receiver_Header: sp->buf_len+=cnt; if (sp->buf_len==DIAMETER_HEADER_LEN){ sp->length = get_3bytes(sp->buf+1); if (sp->length>DP_MAX_MSG_LENGTH){ LM_ERR("do_receive(): [%.*s] Msg too big [%d] bytes\n", sp->p?sp->p->fqdn.len:0, sp->p?sp->p->fqdn.s:0, sp->length); goto error_and_reset; } LM_DBG("receive_loop(): [%.*s] Recv Version %d Length %d\n", sp->p?sp->p->fqdn.len:0, sp->p?sp->p->fqdn.s:0, (unsigned char)(sp->buf[0]), sp->length); sp->msg = shm_malloc(sp->length); if (!sp->msg) { LOG_NO_MEM("shm",sp->length); goto error_and_reset; } memcpy(sp->msg,sp->buf,sp->buf_len); sp->msg_len=sp->buf_len; sp->state = Receiver_Rest_of_Message; } break; case Receiver_Rest_of_Message: sp->msg_len+=cnt; if (sp->msg_len==sp->length){ dmsg = AAATranslateMessage((unsigned char*)sp->msg,(unsigned int)sp->msg_len,1); if (dmsg) { sp->msg = 0; receive_message(dmsg,sp); } else { shm_free(sp->msg); sp->msg = 0; } sp->msg_len = 0; sp->buf_len = 0; sp->state = Receiver_Waiting; } break; default: LM_ERR("do_receive(): [%.*s] Unknown state %d\n", sp->p?sp->p->fqdn.len:0, sp->p?sp->p->fqdn.s:0, sp->state); goto error_and_reset; } return 1; error_and_reset: if (sp->msg){ shm_free(sp->msg); sp->msg = 0; sp->msg_len = 0; sp->buf_len = 0; sp->state = Receiver_Waiting; } return 0; }