/********************************************************************************* * Copyright: (C) 2014 Guo Wenxue * All rights reserved. * * Filename: cp_socket.c * Description: This file * * Version: 1.0.0(11/18/2014) * Author: Guo Wenxue * ChangeLog: 1, Release initial version on "11/18/2014 11:15:04 PM" * ********************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cp_logger.h" #include "cp_socket.h" int cp_sock_block_connect(cp_socket_t *sock, char *servaddr, unsigned short servport) { int rv = -1; int sock_fd = -1; char service[20]; struct addrinfo hints, *rp; struct addrinfo *res = NULL; struct in_addr inaddr; char ipstr[20]; int len; if(!sock || !servaddr || !servport ) { log_err("Invalid input argument\n"); return -1; } if(SOCK_STAT_CONNECTED == sock->status) { return 0; } len = strlen(servaddr); len = len>DOMAIN_MAX_LEN ? DOMAIN_MAX_LEN : len; memcpy(sock->servaddr, servaddr, strlen(servaddr)); sock->servport = servport; log_nrml("Start socket connect to [%s:%d]...\n", sock->servaddr, sock->servport); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; /* Only support IPv4 */ hints.ai_socktype = SOCK_STREAM; /* If Hostname is a valid IP address, then don't use name resolution */ if( inet_aton(sock->servaddr, &inaddr) ) { log_info("%s is a valid IP address, don't use name resolution.\n", sock->servaddr); hints.ai_flags |= AI_NUMERICHOST; } /* Obtain address(es) matching servaddr/servport */ snprintf(service, sizeof(service), "%d", sock->servport); if( (rv=getaddrinfo(sock->servaddr, service, &hints, &res)) ) { log_err("getaddrinfo() parser [%s:%s] failed: %s\n", sock->servaddr, service, gai_strerror(rv)); rv = -2; goto out; } /* getaddrinfo() returns a list of address structures. Try each address until connected */ rv = -3; /* default return value set be connect failure */ sock->status = SOCK_STAT_DISCONNECT; for (rp=res; rp!=NULL; rp=rp->ai_next) { /* create the socket */ sock_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if( sock_fd < 0) { log_err("socket() create failed: %s\n", strerror(errno)); rv = -4; continue; } inet_ntop(AF_INET, &(((struct sockaddr_in *)(rp->ai_addr))->sin_addr), ipstr, sizeof(ipstr)); log_dbg("DNS resolve IP address [%s]\n", ipstr); /* connect to the remote server successfully */ if(0==connect(sock_fd, rp->ai_addr, rp->ai_addrlen)) { memcpy(&sock->saddr, &rp->ai_addr, sizeof(sock->saddr)); sock->fd = sock_fd; sock->status = SOCK_STAT_CONNECTED; rv = 0; goto out; } else /* Connect to server failed. */ { rv = -5; log_err("connect() to server IP[%s] failed: %s\n", ipstr, strerror(errno)); } close(sock_fd); } out: if( rv < 0 ) { log_err("create socket connect to [%s:%d] failed: %s\n", sock->servaddr, sock->servport, strerror(errno)); sock->status = SOCK_STAT_DISCONNECT; sock->fd = -1; } else { log_nrml("create socket connect to [%s:%d] successfully\n", sock->servaddr, sock->servport); sock->status = SOCK_STAT_CONNECTED; } if(res) { freeaddrinfo(res); } return rv; } int cp_sock_nonblock_connect(cp_socket_t *sock, char *servaddr, unsigned short servport) { int rv = -1; int sock_fd = -1; char service[20]; struct addrinfo hints, *rp; struct addrinfo *res = NULL; struct in_addr inaddr; char ipstr[20]; if( !sock || !servaddr || !servport ) { log_err("Invalid input argument\n"); return -1; } if(SOCK_STAT_CONNECTED == sock->status) { return 0; } else if(SOCK_STAT_CONNECTING == sock->status) { goto connecting; } log_nrml("Start socket connect to [%s:%d]...\n", sock->servaddr, sock->servport); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; /* Only support IPv4 */ hints.ai_socktype = SOCK_STREAM; /* If Hostname is a valid IP address, then don't use name resolution */ if( inet_aton(sock->servaddr, &inaddr) ) { log_info("%s is a valid IP address, don't use name resolution.\n", sock->servaddr); hints.ai_flags |= AI_NUMERICHOST; } /* Obtain address(es) matching servaddr/servport */ snprintf(service, sizeof(service), "%d", sock->servport); if( (rv=getaddrinfo(sock->servaddr, service, &hints, &res)) ) { log_err("getaddrinfo() parser [%s:%s] failed: %s\n", sock->servaddr, service, gai_strerror(rv)); rv = -2; goto failed; } /* getaddrinfo() returns a list of address structures. Try each * address until we successfully connect or bind */ rv = -3; /* Connect failure */ for (rp=res; rp!=NULL; rp=rp->ai_next) { /* Create the socket */ sock_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if( sock_fd < 0) { log_err("socket() create failed: %s\n", strerror(errno)); rv = -4; continue; } inet_ntop(AF_INET, &(((struct sockaddr_in *)(rp->ai_addr))->sin_addr), ipstr, sizeof(ipstr)); log_dbg("DNS resolve IP address [%s]\n", ipstr); cp_sock_set_nonblock(sock_fd); /* Nono block Connect to the remote server */ if(0==connect(sock_fd, rp->ai_addr, rp->ai_addrlen)) { /* Conenct to server successfully */ memcpy(&sock->saddr, &rp->ai_addr, sizeof(sock->saddr)); sock->fd = sock_fd; sock->status = SOCK_STAT_CONNECTED; rv = 0; goto connected; } else { if(EINPROGRESS == errno) { /* Connect to server now in progress */ memcpy(&sock->saddr, &rp->ai_addr, sizeof(sock->saddr)); sock->fd = sock_fd; sock->status = SOCK_STAT_CONNECTING; rv = 0; goto connecting; } else { /* Connect to server failed. */ close(sock_fd); rv = -5; log_err("connect() to server failed: %s\n", strerror(errno)); goto failed; } } close(sock_fd); } failed: log_err("create socket connect to [%s:%d] failed: %s\n", sock->servaddr, sock->servport, strerror(errno)); sock->status = SOCK_STAT_DISCONNECT; sock->fd = -1; return rv; connecting: if(SOCK_STAT_CONNECTING == sock->status) { int len; len = sizeof(sock->saddr); errno = 0; if( 0 == connect(sock->fd, &sock->saddr, len) ) { /* connect() return 0 means it already connected */ sock->status = SOCK_STAT_CONNECTED; rv = 0; goto connected; } /* Connect failure will continue to check */ switch (errno) { case EISCONN: sock->status = SOCK_STAT_CONNECTED; rv = 0; goto connected; case EALREADY: case EINPROGRESS: log_dbg("socket[%d] connect to remote [%s:%d] in progress\n", sock->fd, sock->servaddr, sock->servport); rv = 0; goto cleanup; default: log_err("socket[%d] connect to remote [%s:%d] failed: %s\n", sock->fd, sock->servaddr, sock->servport, strerror(errno)); sock->status = SOCK_STAT_DISCONNECT; rv = -7; break; } } connected: if(SOCK_STAT_CONNECTED == sock->status) { rv = 0; log_nrml("socket[%d] connected to remote server [%s:%d]\n", sock->fd, sock->servaddr, sock->servport); goto cleanup; } cleanup: if(res) freeaddrinfo(res); /* No longer needed */ return rv; } int cp_sock_set_nonblock(int sockfd) { int opts; /* * fcntl may set: * * EACCES, EAGAIN: Operation is prohibited by locks held by other * processes. Or, operation is prohibited because the file has * been memory-mapped by another process. * EBADF: fd is not an open file descriptor, or the command was F_SETLK * or F_SETLKW and the file descriptor open mode doesn't match * with the type of lock requested. * EDEADLK: It was detected that the specified F_SETLKW command would * cause a deadlock. * EFAULT: lock is outside your accessible address space. * EINTR: For F_SETLKW, the command was interrupted by a signal. For * F_GETLK and F_SETLK, the command was interrupted by a signal * before the lock was checked or acquired. Most likely when * locking a remote file (e.g. locking over NFS), but can * sometimes happen locally. * EINVAL: For F_DUPFD, arg is negative or is greater than the maximum * allowable value. For F_SETSIG, arg is not an allowable signal * number. * EMFILE: For F_DUPFD, the process already has the maximum number of * file descriptors open. * ENOLCK: Too many segment locks open, lock table is full, or a remote * locking protocol failed (e.g. locking over NFS). * EPERM: Attempted to clear the O_APPEND flag on a file that has the * append-only attribute set. */ opts = fcntl(sockfd, F_GETFL); if (opts < 0) { log_warn("fcntl() get socket options failure: %s\n", strerror(errno)); return -1; } opts |= O_NONBLOCK; if (fcntl(sockfd, F_SETFL, opts) < 0) { log_warn("fcntl() set socket options failure: %s\n", strerror(errno)); return -1; } log_dbg("Set socket[%d] none blocking\n", sockfd); return opts; } void cp_sock_close(cp_socket_t *sock) { close(sock->fd); sock->fd = 0; sock->status = SOCK_STAT_INIT; }