ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
mdns.c
Go to the documentation of this file.
1
9#include <ascii-chat/network/mdns/mdns.h>
10#include <ascii-chat/common.h>
11#include <ascii-chat/platform/socket.h>
12#include <string.h>
13#include <stdio.h>
14#include <ascii-chat-deps/mdns/mdns.h>
15#ifdef _WIN32
16#include <winsock2.h>
17#include <ws2tcpip.h>
18#else
19#include <netinet/in.h> /* For struct sockaddr_in, sockaddr_in6 */
20#endif
21
25 asciichat_mdns_discovery_callback_fn callback;
27 uint8_t *buffer;
29 uint16_t query_id;
30};
31
33#define MDNS_BUFFER_SIZE (4 * 1024)
34
36 asciichat_mdns_t *mdns = SAFE_MALLOC(sizeof(asciichat_mdns_t), asciichat_mdns_t *);
37 if (!mdns) {
38 SET_ERRNO(ERROR_MEMORY, "Failed to allocate mDNS context");
39 return NULL;
40 }
41
42 memset(mdns, 0, sizeof(asciichat_mdns_t));
43
44 /* Allocate I/O buffer for mDNS packets */
45 mdns->buffer = SAFE_MALLOC(MDNS_BUFFER_SIZE, uint8_t *);
46 if (!mdns->buffer) {
47 SAFE_FREE(mdns);
48 SET_ERRNO(ERROR_MEMORY, "Failed to allocate mDNS buffer");
49 return NULL;
50 }
52
53 /* Open IPv4 mDNS socket */
54 mdns->socket_fd = mdns_socket_open_ipv4(NULL);
55 if (mdns->socket_fd < 0) {
56 SAFE_FREE(mdns->buffer);
57 SAFE_FREE(mdns);
58 SET_ERRNO(ERROR_NETWORK_BIND, "Failed to open mDNS socket");
59 return NULL;
60 }
61
62 log_dev("mDNS context initialized (socket: %d, buffer: %zu bytes)", mdns->socket_fd, mdns->buffer_capacity);
63 return mdns;
64}
65
67 if (!mdns) {
68 return;
69 }
70
71 if (mdns->socket_fd >= 0) {
72 mdns_socket_close(mdns->socket_fd);
73 }
74
75 SAFE_FREE(mdns->buffer);
76 SAFE_FREE(mdns);
77 log_debug("mDNS context shutdown");
78}
79
80asciichat_error_t asciichat_mdns_advertise(asciichat_mdns_t *mdns, const asciichat_mdns_service_t *service) {
81 if (!mdns || !service) {
82 return SET_ERRNO(ERROR_INVALID_PARAM, "mDNS context or service is NULL");
83 }
84
85 if (!service->name || !service->type || !service->host) {
86 return SET_ERRNO(ERROR_INVALID_PARAM, "Service name, type, or host is NULL");
87 }
88
89 log_debug("Advertising mDNS service: %s (%s:%d)", service->name, service->host, service->port);
90
91 /* TODO: Implement actual advertisement using mdns library
92 * This will involve creating service records and sending announcements
93 * The mdns library provides mdns_announce_* functions for this
94 */
95 return ASCIICHAT_OK;
96}
97
98asciichat_error_t asciichat_mdns_unadvertise(asciichat_mdns_t *mdns, const char *service_name) {
99 if (!mdns || !service_name) {
100 return SET_ERRNO(ERROR_INVALID_PARAM, "mDNS context or service name is NULL");
101 }
102
103 log_info("Stopped advertising service: %s", service_name);
104
105 /* TODO: Implement actual unadvertisement
106 * This will involve sending goodbye records with TTL=0
107 */
108 return ASCIICHAT_OK;
109}
110
117static int mdns_record_callback(int sock, const struct sockaddr *from, size_t addrlen, mdns_entry_type_t entry,
118 uint16_t query_id, uint16_t rtype, uint16_t rclass, uint32_t ttl, const void *data,
119 size_t size, size_t name_offset, size_t name_length, size_t record_offset,
120 size_t record_length, void *user_data) {
121 asciichat_mdns_t *mdns = (asciichat_mdns_t *)user_data;
122
123 if (!mdns || !mdns->callback) {
124 return 0;
125 }
126
127 /* Only process answers */
128 if (entry != MDNS_ENTRYTYPE_ANSWER) {
129 return 0;
130 }
131
132 asciichat_mdns_discovery_t discovery;
133 memset(&discovery, 0, sizeof(discovery));
134 discovery.ttl = ttl;
135
136 /* Extract service type and name from DNS name format
137 * For PTR records, data contains the target name (service instance name)
138 */
139 size_t offset = name_offset;
140 mdns_string_extract(data, size, &offset, discovery.type, sizeof(discovery.type));
141
142 /* Parse record based on type */
143 switch (rtype) {
144 case MDNS_RECORDTYPE_PTR: {
145 /* PTR record: points to a service instance (e.g., "MyChat" for "_ascii-chat._tcp.local")
146 * The record data contains the target name (the service instance name)
147 */
148 offset = record_offset;
149 mdns_string_extract(data, size, &offset, discovery.name, sizeof(discovery.name));
150 log_debug("mDNS PTR: %s -> %s (TTL: %u)", discovery.type, discovery.name, ttl);
151 break;
152 }
153
154 case MDNS_RECORDTYPE_SRV: {
155 /* SRV record: service details (port, target host)
156 * mdns_record_parse_srv extracts the target hostname into a buffer
157 * and also contains priority, weight, port in the returned structure
158 */
159 mdns_record_srv_t srv =
160 mdns_record_parse_srv(data, size, record_offset, record_length, discovery.host, sizeof(discovery.host));
161 discovery.port = srv.port;
162 log_debug("mDNS SRV: %s -> %s:%u (TTL: %u)", discovery.type, discovery.host, srv.port, ttl);
163 break;
164 }
165
166 case MDNS_RECORDTYPE_A: {
167 /* A record: IPv4 address
168 * mdns_record_parse_a fills a sockaddr_in structure with the address
169 */
170 struct sockaddr_in addr_in;
171 mdns_record_parse_a(data, size, record_offset, record_length, &addr_in);
172
173 /* Convert network address to dotted decimal notation */
174 uint8_t *bytes = (uint8_t *)&addr_in.sin_addr;
175 safe_snprintf(discovery.ipv4, sizeof(discovery.ipv4), "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]);
176 log_debug("mDNS A: %s -> %s (TTL: %u)", discovery.type, discovery.ipv4, ttl);
177 break;
178 }
179
180 case MDNS_RECORDTYPE_AAAA: {
181 /* AAAA record: IPv6 address
182 * mdns_record_parse_aaaa fills a sockaddr_in6 structure with the address
183 */
184 struct sockaddr_in6 addr_in6;
185 mdns_record_parse_aaaa(data, size, record_offset, record_length, &addr_in6);
186
187 /* Convert IPv6 address to colon-separated hex notation */
188 const uint8_t *bytes = (const uint8_t *)&addr_in6.sin6_addr;
189 safe_snprintf(discovery.ipv6, sizeof(discovery.ipv6),
190 "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", bytes[0], bytes[1],
191 bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11],
192 bytes[12], bytes[13], bytes[14], bytes[15]);
193 log_debug("mDNS AAAA: %s -> %s (TTL: %u)", discovery.type, discovery.ipv6, ttl);
194 break;
195 }
196
197 case MDNS_RECORDTYPE_TXT: {
198 /* TXT record: service properties (key=value pairs)
199 * For simplicity, we skip individual TXT parsing for now
200 * Could be extended to extract and store specific properties
201 */
202 log_debug("mDNS TXT: %s (TTL: %u)", discovery.type, ttl);
203 discovery.txt[0] = '\0';
204 break;
205 }
206
207 default:
208 log_debug("mDNS unknown record type: %u", rtype);
209 return 0;
210 }
211
212 /* Call the user callback with discovered service */
213 mdns->callback(&discovery, mdns->callback_data);
214
215 (void)sock;
216 (void)query_id;
217 (void)rclass;
218 (void)addrlen;
219 (void)from;
220 (void)name_length;
221 return 0;
222}
223
224asciichat_error_t asciichat_mdns_query(asciichat_mdns_t *mdns, const char *service_type,
225 asciichat_mdns_discovery_callback_fn callback, void *user_data) {
226 if (!mdns || !service_type || !callback) {
227 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid mDNS query parameters");
228 }
229
230 /* Validate service type is non-empty and has minimum length
231 * Prevents underflow bug in mdns library when processing empty strings
232 */
233 size_t service_type_len = strlen(service_type);
234 if (service_type_len == 0) {
235 return SET_ERRNO(ERROR_INVALID_PARAM, "Service type cannot be empty");
236 }
237
238 mdns->callback = callback;
239 mdns->callback_data = user_data;
240
241 log_info("Starting mDNS query for: %s", service_type);
242
243 /* Send PTR query for service type (one-shot query)
244 * PTR query discovers all instances of a service type
245 * mdns_query_send returns the query ID for response filtering
246 */
247 int query_id = mdns_query_send(mdns->socket_fd, MDNS_RECORDTYPE_PTR, service_type, service_type_len, mdns->buffer,
248 mdns->buffer_capacity, 0);
249
250 if (query_id < 0) {
251 return SET_ERRNO(ERROR_NETWORK, "mDNS query send failed for %s (query_id=%d)", service_type, query_id);
252 }
253
254 mdns->query_id = (uint16_t)query_id;
255 log_debug("mDNS query sent for service type: %s (query_id: %d)", service_type, query_id);
256
257 return ASCIICHAT_OK;
258}
259
260asciichat_error_t asciichat_mdns_update(asciichat_mdns_t *mdns, int timeout_ms) {
261 if (!mdns) {
262 return SET_ERRNO(ERROR_INVALID_PARAM, "mDNS context is NULL");
263 }
264
265 /* Process incoming mDNS packets (responses from previous queries)
266 * mdns_query_recv processes all records received since last call
267 * The callback function (mdns_record_callback) is invoked for each record
268 */
269 int num_records =
270 mdns_query_recv(mdns->socket_fd, mdns->buffer, mdns->buffer_capacity, mdns_record_callback, mdns, mdns->query_id);
271
272 if (num_records < 0) {
273 return SET_ERRNO(ERROR_NETWORK, "Failed to receive mDNS query responses");
274 }
275
276 if (num_records > 0) {
277 log_debug("Processed %d mDNS records", num_records);
278 }
279
280 (void)timeout_ms;
281 return ASCIICHAT_OK;
282}
283
285 if (!mdns) {
286 return -1;
287 }
288 return mdns->socket_fd;
289}
asciichat_error_t asciichat_mdns_update(asciichat_mdns_t *mdns, int timeout_ms)
Definition mdns.c:260
asciichat_error_t asciichat_mdns_unadvertise(asciichat_mdns_t *mdns, const char *service_name)
Definition mdns.c:98
#define MDNS_BUFFER_SIZE
mDNS packet buffer size (4KB should handle most service records)
Definition mdns.c:33
void asciichat_mdns_destroy(asciichat_mdns_t *mdns)
Definition mdns.c:66
asciichat_mdns_t * asciichat_mdns_init(void)
Definition mdns.c:35
int asciichat_mdns_get_socket(asciichat_mdns_t *mdns)
Definition mdns.c:284
asciichat_error_t asciichat_mdns_query(asciichat_mdns_t *mdns, const char *service_type, asciichat_mdns_discovery_callback_fn callback, void *user_data)
Definition mdns.c:224
asciichat_error_t asciichat_mdns_advertise(asciichat_mdns_t *mdns, const asciichat_mdns_service_t *service)
Definition mdns.c:80
Internal mDNS context structure.
Definition mdns.c:23
size_t buffer_capacity
Definition mdns.c:28
int socket_fd
Definition mdns.c:24
asciichat_mdns_discovery_callback_fn callback
Definition mdns.c:25
uint16_t query_id
Definition mdns.c:29
void * callback_data
Definition mdns.c:26
uint8_t * buffer
Definition mdns.c:27
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456