4d263526362d5137f5c616d9a2658b8220d0b0dd
[psmb.git] / src / psmb_socket.c
1 #include <stdlib.h>
2 #include <errno.h>
3 #include <unistd.h>
4 #include <arpa/inet.h>
5 #include <netinet/in.h>
6 #include <sys/socket.h>
7
8 #include <psmb.h>
9 #include "psmb_priv.h"
10
11 static void dummy_log(void *log_priv, int priority, const char *format, ...) {}
12
13 psmb_ctx_t *psmb_new(void)
14 {
15         return psmb_new_mm(malloc, free, realloc);
16 }
17
18 psmb_ctx_t *psmb_new_mm(void *(*malloc)(size_t size),
19                         void (*free)(void *ptr),
20                         void *(*realloc)(void *ptr, size_t size))
21 {
22         psmb_ctx_t *ctx = (*malloc)(sizeof(psmb_ctx_t));
23         if (!ctx)
24                 return NULL;
25         *ctx = (psmb_ctx_t){
26                 .fd = -1,
27                 .malloc = malloc, .free = free, .realloc = realloc,
28                 .logf = dummy_log,
29                 .pmtu = PSMB_DEFAULT_PMTU,
30                 .port = PSMB_DEFAULT_PORT};
31         return ctx;
32 }
33
34 psmb_result_t psmb_set_logf(psmb_ctx_t *ctx,
35         void (*logf)(void *log_priv, int priority, const char *format, ...),
36         void *log_priv)
37 {
38         if (ctx->fd == -1) {
39                 ctx->logf = logf;
40                 ctx->log_priv = log_priv;
41                 return (psmb_result_t){PSMB_OK};
42         } else {
43                 errno = EBUSY;
44                 return (psmb_result_t){PSMB_ERROR};
45         }
46 }
47
48 psmb_result_t psmb_set_pmtu(psmb_ctx_t *ctx, unsigned int pmtu)
49 {
50         if (ctx->fd == -1) {
51                 ctx->pmtu = pmtu;
52                 return (psmb_result_t){PSMB_OK};
53         } else {
54                 errno = EBUSY;
55                 return (psmb_result_t){PSMB_ERROR};
56         }
57 }
58
59 psmb_result_t psmb_set_port(psmb_ctx_t *ctx, unsigned short port)
60 {
61         if (ctx->fd == -1) {
62                 ctx->port = port;
63                 return (psmb_result_t){PSMB_OK};
64         } else {
65                 errno = EBUSY;
66                 return (psmb_result_t){PSMB_ERROR};
67         }
68 }
69
70 psmb_result_t psmb_open(psmb_ctx_t *ctx)
71 {
72         unsigned long on = 1;
73         struct sockaddr_in6 addr = (struct sockaddr_in6){
74                 .sin6_family = AF_INET6,
75                 .sin6_addr = in6addr_any,
76                 .sin6_port = htons(ctx->port)
77         };
78
79         if (ctx->fd != -1) {
80                 errno = EBUSY;
81                 return (psmb_result_t){PSMB_ERROR};
82         }
83         ctx->fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IPV6);
84         if (ctx->fd == -1) {
85                 return (psmb_result_t){PSMB_ERROR};
86         }
87         if (setsockopt(ctx->fd, SOL_SOCKET, SO_REUSEADDR,
88                         &on, sizeof(on)) < 0) {
89                 int sverr = errno;
90                 close(ctx->fd);
91                 ctx->fd = -1;
92                 errno = sverr;
93                 return (psmb_result_t){PSMB_ERROR};
94         }
95         if (setsockopt(ctx->fd, IPPROTO_IPV6, IPV6_PKTINFO,
96                         &on, sizeof(on)) < 0) {
97                 int sverr = errno;
98                 close(ctx->fd);
99                 ctx->fd = -1;
100                 errno = sverr;
101                 return (psmb_result_t){PSMB_ERROR};
102         }
103         if (bind(ctx->fd, (struct sockaddr *)&addr,
104                         sizeof(struct sockaddr)) == -1) {
105                 int sverr = errno;
106                 close(ctx->fd);
107                 ctx->fd = -1;
108                 errno = sverr;
109                 return (psmb_result_t){PSMB_ERROR};
110         }
111         return (psmb_result_t){PSMB_OK};
112 }