/* * $Id$ * * Copyright (C) 2002-2003 Fhg Fokus * * This file is part of sems, a free SIP media server. * * sems is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * sems is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** @file AmUtils.cpp */ #include "AmUtils.h" #include "AmThread.h" #include "AmConfig.h" #include "log.h" #include "AmServer.h" #include "AmSipMsg.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 104 #endif // timeout in us (ms/1000) #define SER_WRITE_TIMEOUT 250000 // 250 ms // write retry interval in us #define SER_WRITE_INTERVAL 50000 // 50 ms // timeout in us (ms/1000) #define SER_SIPREQ_TIMEOUT 5*60*1000*1000 // 5 minutes #define SER_DBREQ_TIMEOUT 250000 // 250 ms // read retry interval in us #define SER_READ_INTERVAL 50000 // 50 ms static char _int2str_lookup[] = { '0', '1', '2', '3', '4', '5', '6' , '7', '8', '9' }; string int2str(int val) { char buffer[64] = {0}; int i=62; div_t d; d.quot = val; do{ d = div(d.quot,10); buffer[i] = _int2str_lookup[d.rem]; }while(--i && d.quot); return string((char*)(buffer+i+1)); } static char _int2hex_lookup[] = { '0', '1', '2', '3', '4', '5', '6' , '7', '8', '9','A','B','C','D','E','F' }; string int2hex(unsigned int val) { unsigned int digit=0; char buffer[2*sizeof(int)+1] = {0}; int i,j=0; for(i=0; i> 4*(2*sizeof(int)-1); val = val << 4; buffer[j++] = _int2hex_lookup[(unsigned char)digit]; } return string((char*)buffer); } string long2hex(unsigned long val) { unsigned int digit=0; char buffer[2*sizeof(long)+1] = {0}; int i,j=0; for(i=0; i> 4*(2*sizeof(long)-1); val = val << 4; buffer[j++] = _int2hex_lookup[(unsigned char)digit]; } return string((char*)buffer); } /** * Convert a reversed hex string to uint. * @param str [in] string to convert. * @param result [out] result integer. * @return true if failed. */ bool reverse_hex2int(const string& str, unsigned int& result) { result=0; char mychar; for (string::const_reverse_iterator pc = str.rbegin(); pc != str.rend(); ++pc) { result <<= 4; mychar=*pc; if ( mychar >='0' && mychar <='9') result += mychar -'0'; else if (mychar >='a' && mychar <='f') result += mychar -'a'+10; else if (mychar >='A' && mychar <='F') result += mychar -'A'+10; else return true; } return false; } bool str2i(const string& str, unsigned int& result) { char* s = (char*)str.c_str(); return str2i(s,result); } bool str2i(char*& str, unsigned int& result, char sep) { unsigned int ret=0; int i=0; char* init = str; for(; (*str != '\0') && (*str == ' '); str++); for(; *str != '\0';str++){ if ( (*str <= '9' ) && (*str >= '0') ){ ret=ret*10+*str-'0'; i++; if (i>10) goto error_digits; } else { bool eol = false; switch(*str){ case 0xd: case 0xa: case 0x0: eol = true; } if( (*str != sep) && !eol ) goto error_char; break; } } result = ret; return false; error_digits: DBG("str2i: too many letters in [%s]\n", init); return true; error_char: DBG("str2i: unexpected char 0x%x in %s\n", *str, init); return true; } int parse_return_code(const char* lbuf, unsigned int& res_code, string& res_msg ) { char res_code_str[4] = {'\0'}; const char* cur=lbuf; // parse xxx code for( int i=0; i<3; i++ ){ if( (*cur >= '0') && (*cur <= '9') ) res_code_str[i] = *cur; else goto error; cur++; } if( (*cur != ' ') && (*cur != '\t') && (*cur !='-') ){ ERROR("expected 0x%x or 0x%x or 0x%x, found 0x%x\n",' ','\t','-',*cur); goto error; } if(sscanf(res_code_str,"%u",&res_code) != 1){ ERROR("wrong code (%s)\n",res_code_str); goto error; } // wrap spaces and tabs while( (*cur == ' ') || (*cur == '\t') || (*cur =='-')) cur++; res_msg = cur; return 0; error: ERROR("while parsing response\n"); return -1; } int fifo_get_line(FILE* fifo_stream, char* str, size_t len) { char c; size_t l; char* s=str; if(!len) return 0; l=len; while( l && (c=fgetc(fifo_stream)) && !ferror(fifo_stream) && c!=EOF && c!='\n' ){ if(c!='\r'){ *(s++) = c; l--; } } if(l>0){ // We need one more character // for trailing '\0'. *s='\0'; return int(s-str); } else // buffer overran. return -1; } int fifo_get_lines(FILE* fifo_stream, char* str, size_t len) { int l=0,max=len; char* s=str; if(!len) return 0; while( max>0 && (l=fifo_get_line(fifo_stream,s,max)) && l!=-1 ) { if(!strcmp(".",s)) break; s+=l; *(s++)='\n'; max-=l+1; } s[0]='\0'; return (l!=-1 ? s-str : -1); } int fifo_get_param(FILE* fp, string& p, char* line_buf, unsigned int size) { if( fifo_get_line(fp,line_buf,size) !=-1 ){ if(!strcmp(".",line_buf)) line_buf[0]='\0'; p = line_buf; } else { ERROR("could not read from FIFO: %s\n",strerror(errno)); return -1; } return 0; } int msg_get_line(char*& msg_c, char* str, size_t len) { size_t l; char* s=str; if(!len) return 0; for(l=len; l && (*msg_c) && (*msg_c !='\n'); msg_c++ ){ *(s++) = *msg_c; l--; } if(*msg_c) msg_c++; if(l>0){ // We need one more character // for trailing '\0'. *s='\0'; return int(s-str); } else { ERROR("buffer too small (size=%u)\n",(unsigned int)len); // buffer overran. return -1; } } int msg_get_lines(char*& msg_c, char* str, size_t len) { int l=0,max=len; char* s=str; if(!len) return 0; while( max>0 && (l=msg_get_line(msg_c,s,max)) && l!=-1 ) { if(!strcmp(".",s) || !strcmp("\r",s)) break; s+=l; *(s++)='\n'; max-=l+1; } s[0]='\0'; return (l!=-1 ? s-str : -1); } int msg_get_param(char*& msg_c, string& p, char* line_buf, unsigned int size) { if( msg_get_line(msg_c,line_buf,size) != -1 ){ if(!strcmp(".",line_buf)) line_buf[0]='\0'; p = line_buf; } else { ERROR("msg_get_line failed\n"); return -1; } return 0; } bool file_exists(const string& name) { FILE* test_fp = fopen(name.c_str(),"r"); if(test_fp){ fclose(test_fp); return true; } return false; } string filename_from_fullpath(const string& path) { string::size_type pos = path.rfind('/'); if(pos != string::npos) return path.substr(pos+1); return ""; } AmMutex inet_ntoa_mut; string get_addr_str(struct in_addr in) { inet_ntoa_mut.lock(); string addr = inet_ntoa(in); inet_ntoa_mut.unlock(); return addr; } AmMutex inet_gethostbyname; string get_ip_from_name(const string& name) { inet_gethostbyname.lock(); struct hostent *he = gethostbyname(name.c_str()); if(!he){ inet_gethostbyname.unlock(); return ""; } struct in_addr a; bcopy(he->h_addr, (char *) &a, sizeof(a)); inet_gethostbyname.unlock(); return get_addr_str(a); } /* Takes a string representation of an IP address, or an FQDN, * and populates the provided struct sockaddr_in (similar to * inet_aton). * Returns the hostent for the input, or NULL on failure. * Almost certainly won't work with IPv6 addresses. */ int populate_sockaddr_in_from_name(const string& name, struct sockaddr_in *sa) { if (NULL == sa) { return 0; } int res = 0; struct addrinfo hints; struct addrinfo *result, *rp; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; // AF_UNSPEC for IPv4 or IPv6 hints.ai_socktype = SOCK_DGRAM; // Datagram socket. hints.ai_flags = AI_ADDRCONFIG; hints.ai_protocol = 0; // Any protocol. int s = getaddrinfo(name.c_str(), NULL, &hints, &result); if (s != 0) { WARN("getaddrinfo failed on %s: %s.\n", name.c_str(), gai_strerror(s)); return res; } for (rp = result; rp != NULL; rp = rp->ai_next) { if ((rp->ai_addrlen != sizeof(struct sockaddr_in)) || // Should not happen. (rp->ai_socktype != SOCK_DGRAM) || (rp->ai_family != AF_INET)) // TODO: Won't behave with IPv6. continue; memcpy(&(sa->sin_addr), &((struct sockaddr_in *)rp->ai_addr)->sin_addr, sizeof(sa->sin_addr)); res = 1; break; } freeaddrinfo(result); return res; } string uri_from_name_addr(const string& name_addr) { string uri = name_addr; string::size_type pos = uri.find('<'); if(pos != string::npos) uri.erase(0,pos+1); pos = uri.find('>'); if(pos != string::npos) uri.erase(pos,uri.length()-pos); return uri; } #ifdef SUPPORT_IPV6 #include int inet_aton_v6(const char* name, struct sockaddr_storage* ss) { int error; //struct sockaddr *sa; struct addrinfo hints; struct addrinfo *res; memset(&hints, 0, sizeof(hints)); /* set-up hints structure */ hints.ai_family = PF_UNSPEC; error = getaddrinfo(name, NULL, &hints, &res); if (error) ERROR("%s\n",gai_strerror(error)); else if (res) { assert( (res->ai_family == PF_INET) || (res->ai_family == PF_INET6) ); memset(ss,0,sizeof(struct sockaddr_storage)); memcpy(ss,res->ai_addr,res->ai_addrlen); freeaddrinfo(res); return 1; } return 0; } void set_port_v6(struct sockaddr_storage* ss, short port) { switch(ss->ss_family){ case PF_INET: ((struct sockaddr_in*)ss)->sin_port = htons(port); break; case PF_INET6: ((struct sockaddr_in6*)ss)->sin6_port = htons(port); break; default: ERROR("unknown address family\n"); assert(0); break; } } short get_port_v6(struct sockaddr_storage* ss) { switch(ss->ss_family){ case PF_INET: return ntohs(((struct sockaddr_in*)ss)->sin_port); case PF_INET6: return ntohs(((struct sockaddr_in6*)ss)->sin6_port); default: ERROR("unknown address family\n"); assert(0); break; } } #endif int create_unix_socket(const string& path) { if(path.empty()){ ERROR("parameter path is empty\n"); return -1; } int sd = socket(PF_UNIX,SOCK_DGRAM,0); if(sd == -1){ ERROR("could not create unix socket: %s\n",strerror(errno)); return -1; } if(path.size() > UNIX_PATH_MAX-1){ ERROR("could not create unix socket: unix socket path is too long\n"); close(sd); return -1; } struct sockaddr_un sock_addr; sock_addr.sun_family = AF_UNIX; strcpy(sock_addr.sun_path,path.c_str()); if(bind(sd,(struct sockaddr *)&sock_addr, sizeof(struct sockaddr_un)) == -1) { ERROR("could not bind unix socket (path=%s): %s\n",path.c_str(),strerror(errno)); close(sd); return -1; } return sd; } string file_extension(const string& path) { string::size_type pos = path.rfind('.'); if(pos == string::npos) return ""; return path.substr(pos+1,string::npos); } string add2path( const string& path, int n_suffix, ...) { va_list ap; string outpath = path; va_start(ap,n_suffix); for(int i=0; i0; retry--){ if((fd_fifo = open(fifo.c_str(), O_WRONLY | O_NONBLOCK)) == -1) { ERROR("while opening %s: %s\n", fifo.c_str(),strerror(errno)); if(retry) sleep_us(50000); } else { break; } } if(!retry) return -1; DBG("write_to_fifo: <%s>\n",buf); int l = write(fd_fifo,buf,len); close(fd_fifo); if(l==-1) ERROR("while writing: %s\n",strerror(errno)); else DBG("Write to fifo: completed\n"); return l; } int write_to_socket(int sd, const char* to_addr, const char * buf, unsigned int len) { int retry = SER_WRITE_TIMEOUT / SER_WRITE_INTERVAL; int ret=-1; struct sockaddr_un ser_addr; memset (&ser_addr, 0, sizeof (ser_addr)); ser_addr.sun_family = AF_UNIX; strncpy(ser_addr.sun_path,to_addr,UNIX_PATH_MAX); DBG("sending: <%.*s>\n",len,buf); for(;retry>0;retry--){ if( (sendto(sd,buf,len,MSG_DONTWAIT, (struct sockaddr*)&ser_addr, sizeof(struct sockaddr_un)) == -1) ) { if(errno == EAGAIN){ if(retry) sleep_us(SER_WRITE_INTERVAL); continue; } ERROR("while sending request to %s: %s\n", ser_addr.sun_path,strerror(errno)); goto error; } break; } if(!retry){ ERROR("timeout while sending request to %s\n",ser_addr.sun_path); goto error; } DBG("write to unix socket: completed\n"); ret = 0; error: // close(sd); // return (ret == -1 ? ret : len); return ret; } string extract_tag(const string& addr) { string::size_type p = addr.find(";tag="); if(p == string::npos) return ""; p += 5/*sizeof(";tag=")*/; string::size_type p_end = p; while(p_end < addr.length()){ if( addr[p_end] == '>' || addr[p_end] == ';' ) break; p_end++; } return addr.substr(p,p_end-p); } bool key_in_list(const string& s_list, const string& key, char list_delim) { size_t pos = 0; size_t pos2 = 0; size_t pos_n = 0; while (pos < s_list.length()) { pos_n = pos2 = s_list.find(list_delim, pos); if (pos2==string::npos) pos2 = s_list.length()-1; while ((pos2>0)&& ((s_list[pos2] == ' ')||(s_list[pos2] == list_delim) ||(s_list[pos2] == '\n'))) pos2--; if (s_list.substr(pos, pos2-pos+1)==key) return true; if (pos_n == string::npos) return false; while ((pos_npos+param_name.length()+1) && hdr_string.substr(++pos, param_name.length())==param_name && hdr_string[pos+param_name.length()] == '=') { size_t pos2 = pos+param_name.length()+1; while(pos2 explode(string s, string e) { vector ret; int iPos = s.find(e, 0); int iLen = e.length(); while (iPos > -1) { if (iPos != 0) ret.push_back(s.substr(0, iPos)); s.erase(0, iPos+iLen); iPos = s.find(e, 0); } if (s != "") ret.push_back(s); return ret; } // Warning: static var is not mutexed // Call this func only in init code. // void add_env_path(const char* name, const string& path) { string var(path); char* old_path=0; regex_t path_reg; assert(name); if((old_path = getenv(name)) != 0) { if(strlen(old_path)){ if(regcomp(&path_reg,("[:|^]" + path + "[:|$]").c_str(),REG_NOSUB)){ ERROR("could not compile regex\n"); return; } if(!regexec(&path_reg,old_path,0,0,0)) { // match regfree(&path_reg); return; // do nothing } regfree(&path_reg); var += ":" + string(old_path); } } DBG("setting %s to: '%s'\n",name,var.c_str()); setenv(name,var.c_str(),1); }