1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #include <stdio.h> // For printf()
19 #include <stdlib.h> // For exit() etc.
20 #include <string.h> // For strlen() etc.
21 #include <unistd.h> // For select()
22 #include <signal.h> // For SIGINT, SIGTERM
23 #include <errno.h> // For errno, EINTR
24 #include <netinet/in.h> // For INADDR_NONE
25 #include <arpa/inet.h> // For inet_addr()
26 #include <netdb.h> // For gethostbyname()
28 #include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above
29 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
30 #include "ExampleClientApp.h"
32 // Compatibility workaround: Solaris 2.5 has no INADDR_NONE
34 #define INADDR_NONE (mDNSu32)0xffffffff
37 //*************************************************************************************************************
39 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
40 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
41 mDNSexport
const char ProgramName
[] = "mDNSProxyResponderPosix";
43 //*************************************************************************************************************
44 // Proxy Host Registration
49 domainlabel hostlabel
; // Conforms to standard DNS letter-digit-hyphen host name rules
50 AuthRecord RR_A
; // 'A' (address) record for our ".local" name
51 AuthRecord RR_PTR
; // PTR (reverse lookup) record
54 mDNSlocal
void HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
56 ProxyHost
*f
= (ProxyHost
*)rr
->RecordContext
;
57 if (result
== mStatus_NoError
)
58 debugf("Host name successfully registered: %##s", rr
->resrec
.name
->c
);
61 debugf("Host name conflict for %##s", rr
->resrec
.name
->c
);
62 mDNS_Deregister(m
, &f
->RR_A
);
63 mDNS_Deregister(m
, &f
->RR_PTR
);
68 mDNSlocal mStatus
mDNS_RegisterProxyHost(mDNS
*m
, ProxyHost
*p
)
72 mDNS_SetupResourceRecord(&p
->RR_A
, mDNSNULL
, mDNSInterface_Any
, kDNSType_A
, 60, kDNSRecordTypeUnique
, AuthRecordAny
, HostNameCallback
, p
);
73 mDNS_SetupResourceRecord(&p
->RR_PTR
, mDNSNULL
, mDNSInterface_Any
, kDNSType_PTR
, 60, kDNSRecordTypeKnownUnique
, AuthRecordAny
, HostNameCallback
, p
);
75 p
->RR_A
.namestorage
.c
[0] = 0;
76 AppendDomainLabel(&p
->RR_A
.namestorage
, &p
->hostlabel
);
77 AppendLiteralLabelString(&p
->RR_A
.namestorage
, "local");
79 // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
80 mDNS_snprintf(buffer
, sizeof(buffer
), "%d.%d.%d.%d.in-addr.arpa.", p
->ip
.b
[3], p
->ip
.b
[2], p
->ip
.b
[1], p
->ip
.b
[0]);
81 MakeDomainNameFromDNSNameString(&p
->RR_PTR
.namestorage
, buffer
);
82 p
->RR_PTR
.ForceMCast
= mDNStrue
; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server
84 p
->RR_A
.resrec
.rdata
->u
.ipv4
= p
->ip
;
85 AssignDomainName(&p
->RR_PTR
.resrec
.rdata
->u
.name
, p
->RR_A
.resrec
.name
);
87 mDNS_Register(m
, &p
->RR_A
);
88 mDNS_Register(m
, &p
->RR_PTR
);
90 debugf("Made Proxy Host Records for %##s", p
->RR_A
.resrec
.name
->c
);
92 return(mStatus_NoError
);
95 //*************************************************************************************************************
96 // Service Registration
98 // This sample ServiceCallback just calls mDNS_RenameAndReregisterService to automatically pick a new
99 // unique name for the service. For a device such as a printer, this may be appropriate.
100 // For a device with a user interface, and a screen, and a keyboard, the appropriate
101 // response may be to prompt the user and ask them to choose a new name for the service.
102 mDNSlocal
void ServiceCallback(mDNS
*const m
, ServiceRecordSet
*const sr
, mStatus result
)
106 case mStatus_NoError
: debugf("Callback: %##s Name Registered", sr
->RR_SRV
.resrec
.name
->c
); break;
107 case mStatus_NameConflict
: debugf("Callback: %##s Name Conflict", sr
->RR_SRV
.resrec
.name
->c
); break;
108 case mStatus_MemFree
: debugf("Callback: %##s Memory Free", sr
->RR_SRV
.resrec
.name
->c
); break;
109 default: debugf("Callback: %##s Unknown Result %ld", sr
->RR_SRV
.resrec
.name
->c
, result
); break;
112 if (result
== mStatus_NoError
)
114 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
115 ConvertDomainNameToCString(sr
->RR_SRV
.resrec
.name
, buffer
);
116 printf("Service %s now registered and active\n", buffer
);
119 if (result
== mStatus_NameConflict
)
121 char buffer1
[MAX_ESCAPED_DOMAIN_NAME
], buffer2
[MAX_ESCAPED_DOMAIN_NAME
];
122 ConvertDomainNameToCString(sr
->RR_SRV
.resrec
.name
, buffer1
);
123 mDNS_RenameAndReregisterService(m
, sr
, mDNSNULL
);
124 ConvertDomainNameToCString(sr
->RR_SRV
.resrec
.name
, buffer2
);
125 printf("Name Conflict! %s renamed as %s\n", buffer1
, buffer2
);
129 // RegisterService() is a simple wrapper function which takes C string
130 // parameters, converts them to domainname parameters, and calls mDNS_RegisterService()
131 mDNSlocal
void RegisterService(mDNS
*m
, ServiceRecordSet
*recordset
,
132 const char name
[], const char type
[], const char domain
[],
133 const domainname
*host
, mDNSu16 PortAsNumber
, int argc
, char **argv
)
137 unsigned char txtbuffer
[1024], *bptr
= txtbuffer
;
138 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
140 MakeDomainLabelFromLiteralString(&n
, name
);
141 MakeDomainNameFromDNSNameString(&t
, type
);
142 MakeDomainNameFromDNSNameString(&d
, domain
);
145 int len
= strlen(argv
[0]);
146 if (len
> 255 || bptr
+ 1 + len
>= txtbuffer
+ sizeof(txtbuffer
)) break;
147 printf("STR: %s\n", argv
[0]);
149 strcpy((char*)(bptr
+1), argv
[0]);
155 mDNS_RegisterService(m
, recordset
,
156 &n
, &t
, &d
, // Name, type, domain
157 host
, mDNSOpaque16fromIntVal(PortAsNumber
),
158 txtbuffer
, bptr
-txtbuffer
, // TXT data, length
159 mDNSNULL
, 0, // Subtypes
160 mDNSInterface_Any
, // Interface ID
161 ServiceCallback
, mDNSNULL
, 0); // Callback, context, flags
163 ConvertDomainNameToCString(recordset
->RR_SRV
.resrec
.name
, buffer
);
164 printf("Made Service Records for %s\n", buffer
);
167 //*************************************************************************************************************
168 // Service non-existence assertion
169 // (claiming a service name without actually providing a service at that name, to prevent anyone else using that name)
170 // This is useful to avoid confusion between similar services
171 // e.g. A printer that implements IPP printing service using the name "My Printer", but doesn't implement LPR service,
172 // should also claim the LPR service name "My Printer" to stop a different printer offering LPR service under the same name,
173 // since it would be confusing to users to have two equivalent services with the same name.
175 mDNSlocal
void NoSuchServiceCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
177 const domainname
*proxyhostname
= (const domainname
*)rr
->RecordContext
;
180 case mStatus_NoError
: debugf("Callback: %##s Name Registered", rr
->resrec
.name
->c
); break;
181 case mStatus_NameConflict
: debugf("Callback: %##s Name Conflict", rr
->resrec
.name
->c
); break;
182 case mStatus_MemFree
: debugf("Callback: %##s Memory Free", rr
->resrec
.name
->c
); break;
183 default: debugf("Callback: %##s Unknown Result %ld", rr
->resrec
.name
->c
, result
); break;
186 if (result
== mStatus_NoError
)
188 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
189 ConvertDomainNameToCString(rr
->resrec
.name
, buffer
);
190 printf("Non-existence assertion %s now registered and active\n", buffer
);
193 if (result
== mStatus_NameConflict
)
197 char buffer1
[MAX_ESCAPED_DOMAIN_NAME
], buffer2
[MAX_ESCAPED_DOMAIN_NAME
];
198 ConvertDomainNameToCString(rr
->resrec
.name
, buffer1
);
199 DeconstructServiceName(rr
->resrec
.name
, &n
, &t
, &d
);
200 IncrementLabelSuffix(&n
, mDNStrue
);
201 mDNS_RegisterNoSuchService(m
, rr
, &n
, &t
, &d
, proxyhostname
, mDNSInterface_Any
, NoSuchServiceCallback
, mDNSNULL
, 0);
202 ConvertDomainNameToCString(rr
->resrec
.name
, buffer2
);
203 printf("Name Conflict! %s renamed as %s\n", buffer1
, buffer2
);
207 mDNSlocal
void RegisterNoSuchService(mDNS
*m
, AuthRecord
*const rr
, domainname
*proxyhostname
,
208 const char name
[], const char type
[], const char domain
[])
212 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
213 MakeDomainLabelFromLiteralString(&n
, name
);
214 MakeDomainNameFromDNSNameString(&t
, type
);
215 MakeDomainNameFromDNSNameString(&d
, domain
);
216 mDNS_RegisterNoSuchService(m
, rr
, &n
, &t
, &d
, proxyhostname
, mDNSInterface_Any
, NoSuchServiceCallback
, proxyhostname
, 0);
217 ConvertDomainNameToCString(rr
->resrec
.name
, buffer
);
218 printf("Made Non-existence Record for %s\n", buffer
);
221 //*************************************************************************************************************
224 mDNSexport
int main(int argc
, char **argv
)
229 if (argc
< 3) goto usage
;
231 status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
232 mDNS_Init_NoCache
, mDNS_Init_ZeroCacheSize
,
233 mDNS_Init_DontAdvertiseLocalAddresses
,
234 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
235 if (status
) { fprintf(stderr
, "Daemon start: mDNS_Init failed %d\n", (int)status
); return(status
); }
237 mDNSPosixListenForSignalInEventLoop(SIGINT
);
238 mDNSPosixListenForSignalInEventLoop(SIGTERM
);
240 if (!strcmp(argv
[1], "-"))
242 domainname proxyhostname
;
243 AuthRecord proxyrecord
;
244 if (argc
< 5) goto usage
;
245 proxyhostname
.c
[0] = 0;
246 AppendLiteralLabelString(&proxyhostname
, argv
[2]);
247 AppendLiteralLabelString(&proxyhostname
, "local");
248 RegisterNoSuchService(&mDNSStorage
, &proxyrecord
, &proxyhostname
, argv
[3], argv
[4], "local.");
253 ServiceRecordSet proxyservice
;
255 proxyhost
.ip
.NotAnInteger
= inet_addr(argv
[1]);
256 if (proxyhost
.ip
.NotAnInteger
== INADDR_NONE
) // INADDR_NONE is 0xFFFFFFFF
258 struct hostent
*h
= gethostbyname(argv
[1]);
259 if (h
) proxyhost
.ip
.NotAnInteger
= *(long*)h
->h_addr
;
261 if (proxyhost
.ip
.NotAnInteger
== INADDR_NONE
) // INADDR_NONE is 0xFFFFFFFF
263 fprintf(stderr
, "%s is not valid host address\n", argv
[1]);
267 MakeDomainLabelFromLiteralString(&proxyhost
.hostlabel
, argv
[2]);
269 mDNS_RegisterProxyHost(&mDNSStorage
, &proxyhost
);
272 RegisterService(&mDNSStorage
, &proxyservice
, argv
[3], argv
[4], "local.",
273 proxyhost
.RR_A
.resrec
.name
, atoi(argv
[5]), argc
-6, &argv
[6]);
278 struct timeval timeout
= { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
279 mDNSBool gotSomething
;
280 mDNSPosixRunEventLoopOnce(&mDNSStorage
, &timeout
, &signals
, &gotSomething
);
282 while ( !( sigismember( &signals
, SIGINT
) || sigismember( &signals
, SIGTERM
)));
284 mDNS_Close(&mDNSStorage
);
289 fprintf(stderr
, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv
[0]);
290 fprintf(stderr
, "ip Real IP address (or valid host name) of the host where the service actually resides\n");
291 fprintf(stderr
, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n");
292 fprintf(stderr
, "srvname Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n");
293 fprintf(stderr
, "srvtype IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n");
294 fprintf(stderr
, "port Port number where the service resides (1-65535)\n");
295 fprintf(stderr
, "txt Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n");
296 fprintf(stderr
, "e.g. %s 169.254.12.34 thehost (just create a dot-local host name)\n", argv
[0]);
297 fprintf(stderr
, "or %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv
[0]);
298 fprintf(stderr
, "or %s - thehost \"My Printer\" _printer._tcp. (assertion of non-existence)\n", argv
[0]);