-
Notifications
You must be signed in to change notification settings - Fork 50
/
Copy pathavahi.c
191 lines (172 loc) · 7.01 KB
/
avahi.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// Version that just forks and execs the commands 'avahi-browse' or 'avahi-publish'
// Using the Avahi API is a nightmare; it's a twisted mess of callbacks between "callbacks", "services", "resolvers", etc.
// March 2024, Phil Karn KA9Q
// Jan 2025 - option to publish through static additions to avahi daemon files /etc/avahi/hosts, /etc/avahi/services
// note - does not remove such entries yet!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <sys/file.h>
#include <stdbool.h>
#include "avahi.h"
#define SERVICES "/etc/avahi/services"
#define HOSTS "/etc/avahi/hosts"
bool Static_avahi;
int avahi_publish_address(char const *dns_name,char const *ip_address_string);
int avahi_publish_service(char const *service_name, char const *service_type, char const *dns_name,int service_port, char const *description, int pid);
int avahi_start(char const *service_name,char const *service_type,int const service_port,char const *dns_name,int address,char const *description,void *sock,size_t *socksize){
if(sock != NULL && socksize != NULL){
// Return sockaddr structure
if(*socksize >= sizeof(struct sockaddr_in)){
struct sockaddr_in *sin = sock;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = htonl(address);
sin->sin_port = htons(service_port);
*socksize = sizeof(struct sockaddr_in);
} else
*socksize = 0;
}
int pid = getpid(); // Advertise the parent's pid, not the child's
if(Static_avahi){
// Just put in /etc/avahi/services
avahi_publish_service(service_name, service_type, dns_name,service_port,description,pid);
} else { // static = no
if(fork() == 0){
#if 0
fprintf(stdout,"avahi-publish-service child pid %d\n",getpid());
#endif
// run "avahi-publish-service --no-fail --host=dns_name service_name service_type service_port description pid hostname" in subprocess
// No need to free the asprintf-allocated strings, we're calling exec anyway
// Turn off warnings about unused return values from asprintf generated by some compilers
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
char *port_string = NULL;
asprintf(&port_string, "%d", service_port);
char *host_string = NULL;
asprintf(&host_string, "--host=%s",dns_name);
char *pid_string = NULL;
asprintf(&pid_string, "pid=%d", pid);
char hostname[sysconf(_SC_HOST_NAME_MAX)];
gethostname(hostname,sizeof(hostname));
char *source_string = NULL;
asprintf(&source_string, "source=%s", hostname);
#pragma GCC diagnostic pop
#if 0
fprintf(stdout,"%s %s %s %s %s %s %s %s %s %s\n",
"avahi-publish-service", "avahi-publish-service", "--no-fail", host_string, service_name, service_type, port_string, description, pid_string, hostname);
#endif
execlp("avahi-publish-service", "avahi-publish-service", "--no-fail", host_string, service_name, service_type, port_string, description, pid_string, source_string, NULL);
perror("exec avahi publish service");
return -1;
}
}
if(address != 0){
char *ip_address_string = NULL; // No need to free, we're calling exec
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
asprintf(&ip_address_string,"%d.%d.%d.%d",(address >> 24) & 0xff, (address >> 16) & 0xff, (address >> 8) & 0xff, address & 0xff);
#pragma GCC diagnostic pop
if(Static_avahi){
// Just put in /etc/avahi/hosts
avahi_publish_address(dns_name,ip_address_string);
} else {
if(fork() == 0){
// run "avahi-publish-address dns_name address", only if an address is specified
#if 0
fprintf(stdout,"avahi start: ip address string = %s\n",ip_address_string);
fprintf(stdout,"avahi-publish-address child pid %d\n",getpid());
fprintf(stdout,"%s %s %s %s\n",
"avahi-publish-address", "avahi-publish-address",dns_name,ip_address_string);
#endif
execlp("avahi-publish-address", "avahi-publish-address",dns_name,ip_address_string, NULL);
perror("exec avahi publish address");
return -1;
}
}
}
return 0;
}
// Publish service in static avahi directory
int avahi_publish_service(char const *service_name, char const *service_type, char const *dns_name,int service_port, char const *description, int pid){
char path_name[1024];
snprintf(path_name,sizeof(path_name),"%s/%s.service",SERVICES,service_name);
FILE *fp = fopen(path_name,"w"); // Will overwrite if exists
if(fp == NULL){
fprintf(stdout,"Can't create %s: %s\n",path_name,strerror(errno));
return -1;
}
char hostname[sysconf(_SC_HOST_NAME_MAX)];
gethostname(hostname,sizeof(hostname));
fcntl(fileno(fp),LOCK_EX);
fputs("<service-group>\n",fp);
fprintf(fp,"<name>%s</name>\n",service_name);
fputs("<service protocol=\"ipv4\">\n",fp);
fprintf(fp,"<host-name>%s</host-name>\n",dns_name);
fprintf(fp,"<type>%s</type>\n",service_type);
fprintf(fp,"<port>%d</port>\n",service_port);
fprintf(fp,"<txt-record>pid=%d</txt-record>\n",pid);
fprintf(fp,"<txt-record>%s</txt-record>\n",description);
fprintf(fp,"<txt-record>source=%s</txt-record>\n",hostname);
fputs("</service>\n",fp);
fputs("</service-group>\n",fp);
fcntl(fileno(fp),LOCK_UN);
fclose(fp);
return 0;
}
// Publish address, host pair in static avahi file
int avahi_publish_address(char const *name,char const *address){
FILE *fp = fopen(HOSTS,"a+");
if(fp == NULL){
fprintf(stdout,"Can't open %s: %s\n",HOSTS,strerror(errno));
return -1;
}
// the lock has to be exclusive even when reading, otherwise there could be double entries
if(flock(fileno(fp),LOCK_EX) != 0){
fprintf(stderr,"Can't lock %s: %s\n",HOSTS,strerror(errno));
fclose(fp);
return -1;
}
rewind(fp); // Start reading at front
char *buffer = NULL;
size_t linecap = 0;
int linelen;
while((linelen = getline(&buffer,&linecap,fp)) > 0){
char *cp;
if((cp = strchr(buffer,'\n')) != NULL)
*cp = '\0';
if(buffer[0] == '#')
continue;
char *line = buffer;
char const *faddr = strsep(&line," \t");
if(faddr == NULL || line == NULL)
continue;
// Skip multiple white space
while(*line == ' ' || *line == '\t')
line++;
char const *fname = line;
if(fname == NULL || *fname == '\0')
continue;
// These are string comparisons so there might be a false mismatch
// if they're written differently. redo as compares of canonical forms
// Also, what are the rules about the same names with different addresses
// and vice versa?
if(strcmp(name,fname) == 0 && strcmp(address,faddr) == 0){
// Yes, entry is already present, don't do anything
flock(fileno(fp),LOCK_UN);
fclose(fp);
free(buffer);
return 0;
}
}
free(buffer);
// we've read the entire file without a match, so append our record
// The "a+" open mode means we'll append
if(fprintf(fp,"%s %s\n",address,name) < 0)
fprintf(stdout,"Can't append to %s: %s\n",HOSTS,strerror(errno));
flock(fileno(fp),LOCK_UN); // Upgrade to exclusive lock for writing
fclose(fp);
return 0;
}