]> www.average.org Git - psmb.git/blob - src/psmb_socket.c
add examples directory with initial example
[psmb.git] / src / psmb_socket.c
1 #include <stdlib.h>
2 #include <errno.h>
3 #include <unistd.h>
4 #include <sys/types.h>
5 #include <arpa/inet.h>
6 #include <netinet/in.h>
7 #include <sys/socket.h>
8
9 #include <psmb.h>
10 #include "psmb_priv.h"
11
12 static void dummy_log(void *log_priv, int priority, const char *format, ...) {}
13
14 psmb_ctx_t *psmb_new(void)
15 {
16         return psmb_new_mm(malloc, free, realloc);
17 }
18
19 psmb_ctx_t *psmb_new_mm(void *(*malloc)(size_t size),
20                         void (*free)(void *ptr),
21                         void *(*realloc)(void *ptr, size_t size))
22 {
23         psmb_ctx_t *ctx = (*malloc)(sizeof(psmb_ctx_t));
24         if (!ctx) {
25                 int sverr = errno;
26                 LOG(ctx, LOG_ERR, "failed to allocate psmb_ctx: %m");
27                 errno = sverr;
28                 return NULL;
29         }
30         *ctx = (psmb_ctx_t){
31                 .fd = -1,
32                 .malloc = malloc, .free = free, .realloc = realloc,
33                 .logf = dummy_log,
34                 .pmtu = PSMB_DEFAULT_PMTU,
35                 .port = PSMB_DEFAULT_PORT};
36         return ctx;
37 }
38
39 psmb_result_t psmb_set_logf(psmb_ctx_t *ctx,
40         void (*logf)(void *log_priv, int priority, const char *format, ...),
41         void *log_priv)
42 {
43         if (ctx->fd == -1) {
44                 ctx->logf = logf;
45                 ctx->log_priv = log_priv;
46                 return (psmb_result_t){PSMB_OK};
47         } else {
48                 LOG(ctx, LOG_ERR, "psmb_set_...() used after psmb_open()");
49                 errno = EBUSY;
50                 return (psmb_result_t){PSMB_ERROR};
51         }
52 }
53
54 psmb_result_t psmb_set_pmtu(psmb_ctx_t *ctx, unsigned int pmtu)
55 {
56         if (ctx->fd == -1) {
57                 ctx->pmtu = pmtu;
58                 return (psmb_result_t){PSMB_OK};
59         } else {
60                 LOG(ctx, LOG_ERR, "psmb_set_...() used after psmb_open()");
61                 errno = EBUSY;
62                 return (psmb_result_t){PSMB_ERROR};
63         }
64 }
65
66 psmb_result_t psmb_set_port(psmb_ctx_t *ctx, unsigned short port)
67 {
68         if (ctx->fd == -1) {
69                 ctx->port = port;
70                 return (psmb_result_t){PSMB_OK};
71         } else {
72                 LOG(ctx, LOG_ERR, "psmb_set_...() used after psmb_open()");
73                 errno = EBUSY;
74                 return (psmb_result_t){PSMB_ERROR};
75         }
76 }
77
78 psmb_result_t psmb_set_mgrp(psmb_ctx_t *ctx, struct in6_addr prefix,
79         unsigned char prefixlen)
80 {
81         if (ctx->fd == -1) {
82                 ctx->prefix = prefix;
83                 ctx->prefixlen = prefixlen;
84                 return (psmb_result_t){PSMB_OK};
85         } else {
86                 LOG(ctx, LOG_ERR, "psmb_set_...() used after psmb_open()");
87                 errno = EBUSY;
88                 return (psmb_result_t){PSMB_ERROR};
89         }
90 }
91
92 psmb_result_t psmb_open(psmb_ctx_t *ctx)
93 {
94         unsigned long on = 1;
95         struct sockaddr_in6 addr = (struct sockaddr_in6){
96                 .sin6_family = AF_INET6,
97                 .sin6_addr = in6addr_any,
98                 .sin6_port = htons(ctx->port)
99         };
100
101         if (ctx->fd != -1) {
102                 LOG(ctx, LOG_ERR, "redundant call to psmb_open()");
103                 errno = EBUSY;
104                 return (psmb_result_t){PSMB_ERROR};
105         }
106         ctx->fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
107         if (ctx->fd == -1) {
108                 int sverr = errno;
109                 LOG(ctx, LOG_ERR, "socket: %m");
110                 errno = sverr;
111                 return (psmb_result_t){PSMB_ERROR};
112         }
113         if (setsockopt(ctx->fd, SOL_SOCKET, SO_REUSEADDR,
114                         &on, sizeof(on)) < 0) {
115                 int sverr = errno;
116                 LOG(ctx, LOG_ERR, "setsockopt(..., SO_REUSEADDR, ...): %m");
117                 close(ctx->fd);
118                 ctx->fd = -1;
119                 errno = sverr;
120                 return (psmb_result_t){PSMB_ERROR};
121         }
122         if (setsockopt(ctx->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
123                         &on, sizeof(on)) < 0) {
124                 int sverr = errno;
125                 LOG(ctx, LOG_ERR, "setsockopt(..., IPV6_PKTINFO, ...): %m");
126                 close(ctx->fd);
127                 ctx->fd = -1;
128                 errno = sverr;
129                 return (psmb_result_t){PSMB_ERROR};
130         }
131         if (bind(ctx->fd, (struct sockaddr *)&addr,
132                         sizeof(struct sockaddr_in6)) == -1) {
133                 int sverr = errno;
134                 LOG(ctx, LOG_ERR, "bind(): %m");
135                 close(ctx->fd);
136                 ctx->fd = -1;
137                 errno = sverr;
138                 return (psmb_result_t){PSMB_ERROR};
139         }
140         return (psmb_result_t){PSMB_OK};
141 }
142
143 void psmb_destroy(psmb_ctx_t *ctx)
144 {
145         if (ctx->fd == -1) {
146                 LOG(ctx, LOG_ERR, "psmb_ctx is not open");
147         } else {
148                 if (close(ctx->fd) == -1)
149                         LOG(ctx, LOG_ERR, "close(): %m");
150         }
151         /* clean up the rest */
152         (*ctx->free)(ctx);
153 }