function to set mgrp (IPv6 prefix and length)
[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_set_mgrp(psmb_ctx_t *ctx, struct in6_addr prefix,
71         unsigned char prefixlen)
72 {
73         if (ctx->fd == -1) {
74                 ctx->prefix = prefix;
75                 ctx->prefixlen = prefixlen;
76                 return (psmb_result_t){PSMB_OK};
77         } else {
78                 errno = EBUSY;
79                 return (psmb_result_t){PSMB_ERROR};
80         }
81 }
82
83 psmb_result_t psmb_open(psmb_ctx_t *ctx)
84 {
85         unsigned long on = 1;
86         struct sockaddr_in6 addr = (struct sockaddr_in6){
87                 .sin6_family = AF_INET6,
88                 .sin6_addr = in6addr_any,
89                 .sin6_port = htons(ctx->port)
90         };
91
92         if (ctx->fd != -1) {
93                 errno = EBUSY;
94                 return (psmb_result_t){PSMB_ERROR};
95         }
96         ctx->fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IPV6);
97         if (ctx->fd == -1) {
98                 return (psmb_result_t){PSMB_ERROR};
99         }
100         if (setsockopt(ctx->fd, SOL_SOCKET, SO_REUSEADDR,
101                         &on, sizeof(on)) < 0) {
102                 int sverr = errno;
103                 close(ctx->fd);
104                 ctx->fd = -1;
105                 errno = sverr;
106                 return (psmb_result_t){PSMB_ERROR};
107         }
108         if (setsockopt(ctx->fd, IPPROTO_IPV6, IPV6_PKTINFO,
109                         &on, sizeof(on)) < 0) {
110                 int sverr = errno;
111                 close(ctx->fd);
112                 ctx->fd = -1;
113                 errno = sverr;
114                 return (psmb_result_t){PSMB_ERROR};
115         }
116         if (bind(ctx->fd, (struct sockaddr *)&addr,
117                         sizeof(struct sockaddr)) == -1) {
118                 int sverr = errno;
119                 close(ctx->fd);
120                 ctx->fd = -1;
121                 errno = sverr;
122                 return (psmb_result_t){PSMB_ERROR};
123         }
124         return (psmb_result_t){PSMB_OK};
125 }