qLibc
qsocket.c
Go to the documentation of this file.
1/******************************************************************************
2 * qLibc
3 *
4 * Copyright (c) 2010-2026 Seungyoung Kim.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *****************************************************************************/
28
29/**
30 * @file qsocket.c Socket handling APIs.
31 */
32
33#ifndef _WIN32
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <stdbool.h>
38#include <string.h>
39#include <stdarg.h>
40#include <netdb.h>
41#include <fcntl.h>
42#include <unistd.h>
43#include <errno.h>
44#include <arpa/inet.h>
45#include <netinet/in.h>
46#include <sys/socket.h>
47#include "qinternal.h"
48#include "utilities/qio.h"
49#include "utilities/qstring.h"
50#include "utilities/qsocket.h"
51
52/**
53 * Create a TCP socket for a remote host and port.
54 *
55 * @param hostname remote hostname
56 * @param port remote port
57 * @param timeoutms wait timeout in milliseconds. If negative, wait forever.
58 *
59 * @return the new socket descriptor, or:
60 * -1 if the hostname is invalid
61 * -2 if socket creation fails
62 * -3 if the connection fails
63 */
64int qsocket_open(const char *hostname, int port, int timeoutms) {
65 /* host conversion */
66 struct sockaddr_in addr;
67 if (qsocket_get_addr(&addr, hostname, port) == false) {
68 return -1; /* invalid hostname */
69 }
70
71 /* create new socket */
72 int sockfd;
73 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
74 return -2; /* sockfd creation fail */
75 }
76
77 /* set to non-block socket*/
78 int flags = fcntl(sockfd, F_GETFL, 0);
79 if (timeoutms >= 0)
80 fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
81
82 /* try to connect */
83 int status = connect(sockfd, (struct sockaddr *) &addr, sizeof(addr));
84 if (status < 0
85 && (errno != EINPROGRESS
86 || qio_wait_writable(sockfd, timeoutms) <= 0)) {
87 close(sockfd);
88 return -3; /* connection failed */
89 }
90
91 /* restore to block socket */
92 if (timeoutms >= 0)
93 fcntl(sockfd, F_SETFL, flags);
94
95 return sockfd;
96}
97
98/**
99 * Close socket.
100 *
101 * @param sockfd socket descriptor
102 * @param timeoutms if `timeoutms >= 0`, shut down the write side first, then
103 * wait and discard incoming data. Set to -1 to close the
104 * socket immediately.
105 *
106 * @return true on success, or false if an error occurred.
107 */
108bool qsocket_close(int sockfd, int timeoutms) {
109 // close connection
110 if (timeoutms >= 0 && shutdown(sockfd, SHUT_WR) == 0) {
111 char buf[1024];
112 while (true) {
113 ssize_t read = qio_read(sockfd, buf, sizeof(buf), timeoutms);
114 DEBUG("Throw %zu bytes from dummy input stream.", read);
115 if (read <= 0)
116 break;
117 }
118 }
119
120 if (close(sockfd) == 0)
121 return true;
122 return false;
123}
124
125/**
126 * Convert a hostname to a `sockaddr_in` structure.
127 *
128 * @param addr `sockaddr_in` output pointer
129 * @param hostname IP address string or hostname
130 * @param port port number
131 *
132 * @return true on success, otherwise false.
133 */
134bool qsocket_get_addr(struct sockaddr_in *addr, const char *hostname, int port) {
135 /* here we assume that the hostname argument contains ip address */
136 memset((void *) addr, 0, sizeof(struct sockaddr_in));
137 if (!inet_aton(hostname, &addr->sin_addr)) { /* fail then try another way */
138 struct hostent *hp;
139 if ((hp = gethostbyname(hostname)) == 0)
140 return false;
141 memcpy(&addr->sin_addr, hp->h_addr, sizeof(struct in_addr));
142 }
143 addr->sin_family = AF_INET;
144 addr->sin_port = htons(port);
145
146 return true;
147}
148
149/**
150 * Get the local IP address.
151 *
152 * @return pointer to `buf` on success, or NULL on failure.
153 */
154char *qsocket_get_localaddr(char *buf, size_t bufsize) {
155 char hostname[63 + 1];
156 if (gethostname(hostname, sizeof(hostname)) != 0)
157 return NULL;
158
159 struct hostent *hostentry = gethostbyname(hostname);
160 if (hostentry == NULL)
161 return NULL;
162
163 char *localip = inet_ntoa(*(struct in_addr *) *hostentry->h_addr_list);
164 if (localip == NULL)
165 return NULL;
166
167 qstrcpy(buf, bufsize, localip);
168 return buf;
169}
170
171#endif /* _WIN32 */
ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms)
Read from a file descriptor.
Definition qio.c:118
int qio_wait_writable(int fd, int timeoutms)
Wait until a file descriptor is ready for writing.
Definition qio.c:87
bool qsocket_get_addr(struct sockaddr_in *addr, const char *hostname, int port)
Convert a hostname to a sockaddr_in structure.
Definition qsocket.c:134
bool qsocket_close(int sockfd, int timeoutms)
Close socket.
Definition qsocket.c:108
int qsocket_open(const char *hostname, int port, int timeoutms)
Create a TCP socket for a remote host and port.
Definition qsocket.c:64
char * qsocket_get_localaddr(char *buf, size_t bufsize)
Get the local IP address.
Definition qsocket.c:154
char * qstrcpy(char *dst, size_t size, const char *src)
Copy src string to dst.
Definition qstring.c:298