Fix changelog target
[nbd.git] / nbdsrv.c
blob508ac27b58eaf2474891c6a09b8236d0d0c9ad1a
1 #include "config.h"
2 #include "nbd-debug.h"
4 #include <nbdsrv.h>
6 #include <assert.h>
7 #include <ctype.h>
8 #include <netdb.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <syslog.h>
13 #include <unistd.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
19 #define LINELEN 256 /**< Size of static buffer used to read the
20 authorization file (yuck) */
22 #include <cliserv.h>
24 bool address_matches(const char* mask, const void* addr, int af, GError** err) {
25 struct addrinfo *res, *aitmp, hints;
26 char *masksep;
27 char privmask[strlen(mask)+1];
28 int masklen;
29 int addrlen = af == AF_INET ? 4 : 16;
31 assert(af == AF_INET || af == AF_INET6);
33 strcpy(privmask, mask);
35 memset(&hints, 0, sizeof(hints));
36 hints.ai_family = AF_UNSPEC;
37 hints.ai_flags = AI_NUMERICHOST;
39 if((masksep = strchr(privmask, '/'))) {
40 *masksep = '\0';
41 masklen = strtol(++masksep, NULL, 10);
42 } else {
43 masklen = addrlen * 8;
46 int e;
47 if((e = getaddrinfo(privmask, NULL, &hints, &res))) {
48 g_set_error(err, NBDS_ERR, NBDS_ERR_GAI, "could not parse netmask line: %s", gai_strerror(e));
49 return false;
51 aitmp = res;
52 while(res) {
53 const uint8_t* byte_s = addr;
54 uint8_t* byte_t;
55 uint8_t mask = 0;
56 int len_left = masklen;
57 if(res->ai_family != af) {
58 goto next;
60 switch(af) {
61 case AF_INET:
62 byte_t = (uint8_t*)(&(((struct sockaddr_in*)(res->ai_addr))->sin_addr));
63 break;
64 case AF_INET6:
65 byte_t = (uint8_t*)(&(((struct sockaddr_in6*)(res->ai_addr))->sin6_addr));
66 break;
68 while(len_left >= 8) {
69 if(*byte_s != *byte_t) {
70 goto next;
72 byte_s++; byte_t++;
73 len_left -= 8;
75 if(len_left) {
76 mask = getmaskbyte(len_left);
77 if((*byte_s & mask) != (*byte_t & mask)) {
78 goto next;
81 freeaddrinfo(aitmp);
82 return true;
83 next:
84 res = res->ai_next;
86 freeaddrinfo(aitmp);
87 return false;
90 uint8_t getmaskbyte(int masklen) {
91 if(masklen >= 8) {
92 return 0xFF;
94 uint8_t retval = 0;
95 for(int i = 7; i + masklen > 7; i--) {
96 retval |= 1 << i;
99 return retval;
102 int authorized_client(CLIENT *opts) {
103 FILE *f ;
104 char line[LINELEN];
105 char *tmp;
106 struct in_addr addr;
107 struct in_addr client;
108 struct in_addr cltemp;
109 int len;
111 if ((f=fopen(opts->server->authname,"r"))==NULL) {
112 msg(LOG_INFO, "Can't open authorization file %s (%s).",
113 opts->server->authname, strerror(errno));
114 return 1 ;
117 while (fgets(line,LINELEN,f)!=NULL) {
118 char* pos;
119 /* Drop comments */
120 if((pos = strchr(line, '#'))) {
121 *pos = '\0';
123 /* Skip whitespace */
124 pos = line;
125 while((*pos) && isspace(*pos)) {
126 pos++;
128 /* Skip content-free lines */
129 if(!(*pos)) {
130 continue;
132 struct sockaddr* sa = (struct sockaddr*)&opts->clientaddr;
133 if(address_matches(line, sa->sa_data, sa->sa_family, NULL)) {
134 fclose(f);
135 return 1;
138 fclose(f);
139 return 0;
143 * duplicate server
144 * @param s the old server we want to duplicate
145 * @return new duplicated server
147 SERVER* dup_serve(const SERVER *const s) {
148 SERVER *serve = NULL;
150 serve=g_new0(SERVER, 1);
151 if(serve == NULL)
152 return NULL;
154 if(s->exportname)
155 serve->exportname = g_strdup(s->exportname);
157 serve->expected_size = s->expected_size;
159 if(s->listenaddr)
160 serve->listenaddr = g_strdup(s->listenaddr);
162 serve->port = s->port;
164 if(s->authname)
165 serve->authname = strdup(s->authname);
167 serve->flags = s->flags;
168 serve->socket = s->socket;
169 serve->socket_family = s->socket_family;
170 serve->virtstyle = s->virtstyle;
171 serve->cidrlen = s->cidrlen;
173 if(s->prerun)
174 serve->prerun = g_strdup(s->prerun);
176 if(s->postrun)
177 serve->postrun = g_strdup(s->postrun);
179 if(s->transactionlog)
180 serve->transactionlog = g_strdup(s->transactionlog);
182 if(s->servename)
183 serve->servename = g_strdup(s->servename);
185 serve->max_connections = s->max_connections;
187 return serve;
190 int append_serve(const SERVER *const s, GArray *const a) {
191 SERVER *ns = NULL;
192 struct addrinfo hints;
193 struct addrinfo *ai = NULL;
194 struct addrinfo *rp = NULL;
195 char host[NI_MAXHOST];
196 gchar *port = NULL;
197 int e;
198 int ret;
200 assert(s != NULL);
201 if(a == NULL) {
202 return -1;
205 port = g_strdup_printf("%d", s->port);
207 memset(&hints,'\0',sizeof(hints));
208 hints.ai_family = AF_UNSPEC;
209 hints.ai_socktype = SOCK_STREAM;
210 hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
211 hints.ai_protocol = IPPROTO_TCP;
213 e = getaddrinfo(s->listenaddr, port, &hints, &ai);
215 if (port)
216 g_free(port);
218 if(e == 0) {
219 for (rp = ai; rp != NULL; rp = rp->ai_next) {
220 e = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
222 if (e != 0) { // error
223 fprintf(stderr, "getnameinfo: %s\n", gai_strerror(e));
224 continue;
227 // duplicate server and set listenaddr to resolved IP address
228 ns = dup_serve (s);
229 if (ns) {
230 ns->listenaddr = g_strdup(host);
231 ns->socket_family = rp->ai_family;
232 g_array_append_val(a, *ns);
233 free(ns);
234 ns = NULL;
238 ret = 0;
239 } else {
240 fprintf(stderr, "getaddrinfo failed on listen host/address: %s (%s)\n", s->listenaddr ? s->listenaddr : "any", gai_strerror(e));
241 ret = -1;
244 if (ai)
245 freeaddrinfo(ai);
247 return ret;
250 uint64_t size_autodetect(int fhandle) {
251 off_t es;
252 u64 bytes __attribute__((unused));
253 struct stat stat_buf;
254 int error;
256 #ifdef HAVE_SYS_MOUNT_H
257 #ifdef HAVE_SYS_IOCTL_H
258 #ifdef BLKGETSIZE64
259 DEBUG("looking for export size with ioctl BLKGETSIZE64\n");
260 if (!ioctl(fhandle, BLKGETSIZE64, &bytes) && bytes) {
261 return bytes;
263 #endif /* BLKGETSIZE64 */
264 #endif /* HAVE_SYS_IOCTL_H */
265 #endif /* HAVE_SYS_MOUNT_H */
267 DEBUG("looking for fhandle size with fstat\n");
268 stat_buf.st_size = 0;
269 error = fstat(fhandle, &stat_buf);
270 if (!error) {
271 /* always believe stat if a regular file as it might really
272 * be zero length */
273 if (S_ISREG(stat_buf.st_mode) || (stat_buf.st_size > 0))
274 return (uint64_t)stat_buf.st_size;
275 } else {
276 DEBUG("fstat failed: %s", strerror(errno));
279 DEBUG("looking for fhandle size with lseek SEEK_END\n");
280 es = lseek(fhandle, (off_t)0, SEEK_END);
281 if (es > ((off_t)0)) {
282 return (uint64_t)es;
283 } else {
284 DEBUG("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
287 DEBUG("Could not find size of exported block device: %s", strerror(errno));
288 return UINT64_MAX;