3 MDU -- Mini DDNS Updater
4 Copyright (C) 2007-2009 Jonathan Zarate
6 Licensed under GNU GPL v2 or later versions.
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <netinet/ip.h>
23 #include <arpa/inet.h>
30 #include <tomato_version.h>
38 #define AGENT "MDU - TEST DDNS CLIENT"
40 #define AGENT "MDU - Tomato Firmware " TOMATO_MAJOR "." TOMATO_MINOR
43 #define MAX_OPTION_LENGTH 256
44 #define BLOB_SIZE (4 * 1024)
47 #define M_UNKNOWN_ERROR__D "Unknown error (%d)."
48 #define M_UNKNOWN_RESPONSE__D "Unknown response (%d)."
49 #define M_INVALID_HOST "Invalid hostname."
50 #define M_INVALID_AUTH "Invalid authentication."
51 #define M_INVALID_PARAM__D "Invalid parameter (%d)."
52 #define M_INVALID_PARAM__S "Invalid parameter (%s)."
53 #define M_TOOSOON "Update was too soon or too frequent. Please try again later."
54 #define M_ERROR_GET_IP "Error obtaining IP address."
55 #define M_SAME_IP "The IP address is the same."
56 #define M_DOWN "Server temporarily down or under maintenance."
59 int error_exitcode
= 1;
68 static void save_cookie(void);
69 static void update_noip_refresh(void);
72 static void trimamp(char *s
)
77 if ((n
> 0) && (s
[--n
] == '&')) s
[n
] = 0;
80 static const char *get_option(const char *name
)
91 if ((c
= get_option("conf")) != NULL
) {
92 if ((f
= fopen(c
, "r")) != NULL
) {
93 while (fgets(s
, sizeof(s
), f
)) {
95 if ((s
[0] == '-') && (s
[1] == '-')) p
+= 2;
96 if ((c
= strchr(p
, ' ')) != NULL
) {
98 if (p
[n
- 1] == '\n') p
[n
- 1] = 0;
101 if (n
<= 0) continue;
102 if (n
>= MAX_OPTION_LENGTH
) exit(88);
104 if ((p
= strdup(p
)) == NULL
) exit(99);
105 f_argv
[f_argc
++] = p
;
106 if (f_argc
>= (sizeof(f_argv
) / sizeof(f_argv
[0]))) break;
115 for (i
= 0; i
< f_argc
; ++i
) {
117 if ((strncmp(c
, name
, n
) == 0) && (c
[n
] == ' ')) {
122 for (i
= 0; i
< g_argc
; ++i
) {
124 if ((p
[0] == '-') && (p
[1] == '-')) {
125 if (strcmp(p
+ 2, name
) == 0) {
127 if ((i
>= g_argc
) || (strlen(g_argv
[i
]) >= MAX_OPTION_LENGTH
)) break;
136 static const char *get_option_safe(const char *name)
138 return get_option(name) ? : "";
142 static const char *get_option_required(const char *name
)
146 if ((p
= get_option(name
)) != NULL
) return p
;
147 fprintf(stderr
, "Required option --%s is missing.\n", name
);
151 static const char *get_option_or(const char *name
, const char *alt
)
153 return get_option(name
) ? : alt
;
156 static int get_option_onoff(const char *name
, int def
)
160 if ((p
= get_option(name
)) == NULL
) return def
;
161 if ((strcmp(p
, "on") == 0) || (strcmp(p
, "1") == 0)) return 1;
162 if ((strcmp(p
, "off") == 0) || (strcmp(p
, "0") == 0)) return 0;
164 fprintf(stderr
, "--%s requires the value off/on or 0/1.\n", name
);
168 static const char *md5_string(const char *value
)
170 static char buf
[(MD5_DIGEST_BYTES
+ 1) * 2];
171 unsigned char digestbuf
[MD5_DIGEST_BYTES
];
174 md5_buffer(value
, strlen(value
), digestbuf
);
175 for (i
= 0; i
< MD5_DIGEST_BYTES
; i
++)
176 sprintf(&buf
[i
* 2], "%02x", digestbuf
[i
]);
181 static void save_msg(const char *s
)
185 if ((path
= get_option("msg")) != NULL
) {
186 f_write_string(path
, s
, FW_NEWLINE
, 0);
190 static void error(const char *fmt
, ...)
196 vsnprintf(s
, sizeof(s
), fmt
, args
);
197 s
[sizeof(s
) - 1] = 0;
200 _dprintf("%s: %s\n", __FUNCTION__
, s
);
204 exit(error_exitcode
);
207 static void success_msg(const char *msg
)
211 _dprintf("%s\n", __FUNCTION__
);
218 static void success(void)
220 success_msg("Update successful.");
225 static const char *get_dump_name(void)
228 return get_option_or("dump", "/tmp/mdu.txt");
230 return get_option("dump");
234 static int _wget(int ssl
, const char *host
, int port
, const char *request
, char *buffer
, int bufsize
, char **body
)
237 struct sockaddr_in sa
;
246 _dprintf("\n*** %s\n", host
);
248 sd
= -1; // for gcc warning
250 for (trys
= 4; trys
> 0; --trys
) {
251 _dprintf("_wget trys=%d\n", trys
);
253 for (i
= 4; i
> 0; --i
) {
254 if ((he
= gethostbyname(host
)) != NULL
) {
255 if ((sd
= socket(AF_INET
, SOCK_STREAM
, 0)) < 0) {
258 memset(&sa
, 0, sizeof(sa
));
259 sa
.sin_family
= AF_INET
;
260 sa
.sin_port
= htons(port
);
261 memcpy(&sa
.sin_addr
, he
->h_addr
, sizeof(sa
.sin_addr
));
265 ia
.s_addr
= sa
.sin_addr
.s_addr
;
267 _dprintf("[%s][%d]\n", inet_ntoa(ia
), port
);
268 _dprintf("connecting...\n");
271 if (connect_timeout(sd
, (struct sockaddr
*)&sa
, sizeof(sa
), 10) == 0) {
272 _dprintf("connected\n");
282 if (i
<= 0) return -1;
286 setsockopt(sd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof(tv
));
287 setsockopt(sd
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof(tv
));
290 mssl_init(NULL
, NULL
);
291 f
= ssl_client_fopen(sd
);
294 f
= fdopen(sd
, "r+");
297 _dprintf("error opening\n");
303 if (fwrite(request
, 1, i
, f
) != i
) {
304 _dprintf("error writing i=%d\n", i
);
309 _dprintf("sent request\n");
311 i
= fread(buffer
, 1, bufsize
, f
);
315 _dprintf("error reading i=%d\n", i
);
320 _dprintf("recvd=[%s], i=%d\n", buffer
, i
);
325 if ((c
= get_dump_name()) != NULL
) {
326 if ((f
= fopen(c
, "a")) != NULL
) {
327 fprintf(f
, "\n[%s:%d]\nREQUEST\n", host
, port
);
329 fputs("\nREPLY\n", f
);
336 if ((sscanf(buffer
, "HTTP/1.%*d %d", &i
) == 1) && (i
>= 100) && (i
<= 999)) {
337 _dprintf("HTTP/1.* i=%d\n", i
);
338 if ((p
= strstr(buffer
, "\r\n\r\n")) != NULL
) p
+= 4;
339 else if ((p
= strstr(buffer
, "\n\n")) != NULL
) p
+= 2;
343 _dprintf("body=[%s]\n", p
);
356 static int wget(int ssl
, int static_host
, const char *host
, const char *get
, const char *header
, int auth
, char **body
)
364 if (!static_host
) host
= get_option_or("server", host
);
367 if (header
) n
+= strlen(header
);
368 if (n
> (BLOB_SIZE
- 512)) return -1; // just don't go over 512 below...
371 "GET %s HTTP/1.0\r\n"
373 "User-Agent: " AGENT
"\r\n",
376 sprintf(a
, "%s:%s", get_option_required("user"), get_option_required("pass"));
377 n
= base64_encode(a
, b
, strlen(a
));
379 sprintf(blob
+ strlen(blob
), "Authorization: Basic %s\r\n", b
);
381 if ((header
) && ((n
= strlen(header
)) > 0)) {
382 strcat(blob
, header
);
383 if (header
[n
- 1] != '\n') strcat(blob
, "\r\n");
385 strcat(blob
, "\r\n");
388 _dprintf("blob=[%s]\n", blob
);
390 port
= ssl
? 443 : 80;
391 strlcpy(a
, host
, sizeof(a
));
392 if ((p
= strrchr(a
, ':')) != NULL
) {
394 if ((n
= atoi(p
+ 1)) > 0) port
= n
;
397 if ((p
= strdup(blob
)) == NULL
) return -1;
398 n
= _wget(ssl
, a
, port
, p
, blob
, BLOB_SIZE
, body
);
401 _dprintf("%s: n=%d\n", __FUNCTION__
, n
);
407 int read_tmaddr(const char *name
, long *tm
, char *addr
)
411 if (f_read_string(name
, s
, sizeof(s
)) > 0) {
412 if (sscanf(s
, "%ld,%15s", tm
, addr
) == 2) {
413 _dprintf("%s: s=%s tm=%ld addr=%s\n", __FUNCTION__
, s
, *tm
, addr
);
414 if ((tm
> 0) && (inet_addr(addr
) != -1)) return 1;
417 _dprintf("%s: unknown=%s\n", __FUNCTION__
, s
);
423 const char *get_address(int required
)
431 static char addr
[16];
434 if ((c
= get_option("addr")) != NULL
) {
437 if ((*c
!= 0) && (strlen(c
) < 20)) {
440 if ((d
= get_option("addrcache")) != NULL
) strlcpy(cache_name
, d
, sizeof(cache_name
));
441 else sprintf(cache_name
, "%s.ip", c
);
442 if (read_tmaddr(cache_name
, &et
, addr
)) {
443 if ((et
> ut
) && ((et
- ut
) <= (10 * 60))) {
444 _dprintf("%s: Using cached address %s from %s. Expires in %ld seconds.\n", __FUNCTION__
, addr
, cache_name
, et
- ut
);
449 if (strcmp(c
, "dyndns") == 0) {
450 if ((wget(0, 1, "checkip.dyndns.org:8245", "/", NULL
, 0, &body
) != 200) &&
451 (wget(0, 1, "checkip.dyndns.org", "/", NULL
, 0, &body
) != 200)) {
452 // Current IP Address: 1.2.3.4
453 error(M_ERROR_GET_IP
);
456 else if (strcmp(c
, "zoneedit") == 0 || strcmp(c
, "szoneedit") == 0) {
457 if (wget(0, 1, "dynamic.zoneedit.com", "/checkip.html", NULL
, 0, &body
) != 200) {
458 // Current IP Address: 1.2.3.4
459 error(M_ERROR_GET_IP
);
462 else if (strcmp(c
, "tzo") == 0) {
463 if ((wget(0, 1, "echo.tzo.com:21333", "/ip.shtml", NULL
, 0, &body
) != 200) &&
464 (wget(0, 1, "echo.tzo.com", "/ip.shtml", NULL
, 0, &body
) != 200)) {
466 error(M_ERROR_GET_IP
);
469 else if (strcmp(c
, "noip") == 0) {
470 if (wget(0, 1, "ip1.dynupdate.no-ip.com", "/", NULL
, 0, &body
) != 200) {
472 error(M_ERROR_GET_IP
);
475 else if (strcmp(c
, "dnsomatic") == 0) {
476 if (wget(0, 1, "myip.dnsomatic.com", "/", NULL
, 0, &body
) != 200) {
478 error(M_ERROR_GET_IP
);
481 else if (strcmp(c
, "pairnic") == 0) {
482 if (wget(0, 1, "myip.pairnic.com", "/", NULL
, 0, &body
) != 200) {
483 // Current IP Address: 1.2.3.4
484 error(M_ERROR_GET_IP
);
487 else if (strcmp(c
, "ovh") == 0) {
488 if (wget(0, 1, "www.ovh.com", "/", NULL
, 0, &body
) != 200) {
489 // Current IP Address: 1.2.3.4
490 error(M_ERROR_GET_IP
);
493 else if (strcmp(c
, "changeip") == 0) {
494 if (wget(0, 1, "nic.changeip.com", "/", NULL
, 0, &body
) != 200) {
495 // Current IP Address: 1.2.3.4
496 error(M_ERROR_GET_IP
);
500 if ((p
= strstr(body
, "Address:")) != NULL
) {
501 // dyndns, zoneedit, tzo, pairnic, ovh, changeip
502 p
+= 8; // note: tzo doesn't have a space
509 while (*p
== ' ') ++p
;
512 while (((*q
>= '0') && (*q
<= '9')) || (*q
== '.')) ++q
;
515 if ((ia
.s_addr
= inet_addr(p
)) != -1) {
517 sprintf(s
, "%ld,%s", ut
+ (10 * 60), p
);
518 f_write_string(cache_name
, s
, 0, 0);
520 _dprintf("%s: saved '%s'\n", __FUNCTION__
, s
);
524 error(M_ERROR_GET_IP
);
529 return required
? get_option_required("addr") : NULL
;
532 static void append_addr_option(char *buffer
, const char *format
)
536 if ((c
= get_address(0)) != NULL
) {
537 sprintf(buffer
+ strlen(buffer
), format
, c
);
541 // -----------------------------------------------------------------------------
547 http://www.dyndns.com/developers/specs/
555 http://test:test@members.dyndns.org/nic/update?system=dyndns&hostname=test.shacknet.nu
559 hostname=yourhost.ourdomain.ext,yourhost2.dyndns.org&
562 mx=mail.exchanger.ext&
566 Host: members.dyndns.org
567 Authorization: Basic username:pass
568 User-Agent: Company - Device - Version Number
571 static void update_dua(const char *type
, int ssl
, const char *server
, const char *path
, int reqhost
)
579 sprintf(query
, "%s?", path
? path
: get_option_required("path"));
582 if (type
) sprintf(query
+ strlen(query
), "system=%s&", type
);
585 p
= reqhost
? get_option_required("host") : get_option("host");
586 if (p
) sprintf(query
+ strlen(query
), "hostname=%s&", p
);
589 if (((p
= get_option("mx")) != NULL
) && (*p
)) {
590 sprintf(query
+ strlen(query
),
594 (get_option_onoff("backmx", 0)) ? "YES" : "NO");
598 append_addr_option(query
, "myip=%s&");
600 if (get_option_onoff("wildcard", 0)) {
601 strcat(query
, "wildcard=ON");
606 r
= wget(ssl
, 0, server
? server
: get_option_required("server"), query
, NULL
, 1, &body
);
609 if ((strstr(body
, "dnserr")) || (strstr(body
, "911"))) {
614 if ((strstr(body
, "badsys")) || (strstr(body
, "numhost")) || (strstr(body
, "ILLEGAL"))) {
615 error(M_INVALID_PARAM__D
, -1);
617 if (strstr(body
, "badagent")) {
618 error(M_INVALID_PARAM__D
, -2);
620 if ((strstr(body
, "badauth")) || (strstr(body
, "NOACCESS")) || (strstr(body
, "!donator"))) {
621 error(M_INVALID_AUTH
);
623 if ((strstr(body
, "notfqdn")) || (strstr(body
, "!yours")) || strstr(body
, "nohost") ||
624 (strstr(body
, "abuse")) || strstr(body
, "NOSERVICE")) {
625 error(M_INVALID_HOST
);
627 if (strstr(body
, "TOOSOON")) {
631 if (strstr(body
, "nochg")) {
632 if ((strcmp(get_option("service"), "noip") == 0) && (refresh
)) {
633 update_noip_refresh();
639 if ((strstr(body
, "good")) || (strstr(body
, "NOERROR"))) {
644 error(M_UNKNOWN_RESPONSE__D
, -1);
647 error(M_INVALID_AUTH
);
650 error(M_UNKNOWN_ERROR__D
, r
);
657 http://namecheap.simplekb.com/kb.aspx?show=article&articleid=27&categoryid=22
661 http://dynamicdns.park-your-domain.com/update?host=host_name&domain=domain.com&password=domain_password[&ip=your_ip]
668 <?xml version="1.0"?>
670 <IP>12.123.123.12</IP>
671 <Command>SETDNSHOST</Command>
672 <Language>eng</Language>
673 <ErrCount>0</ErrCount>
674 <ResponseCount>0</ResponseCount>
675 <MinPeriod>1</MinPeriod>
676 <MaxPeriod>10</MaxPeriod>
677 <Server>Reseller9</Server>
678 <Site>Namecheap</Site>
679 <IsLockable>True</IsLockable>
680 <IsRealTimeTLD>True</IsRealTimeTLD>
681 <TimeDifference>+03.00</TimeDifference>
682 <ExecTime>0.0625</ExecTime>
684 <debug><![CDATA[]]></debug>
685 </interface-response>"
693 <?xml version="1.0"?>
695 <Command>SETDNSHOST</Command>
696 <Language>eng</Language>
697 <ErrCount>1</ErrCount>
699 <Err1>Passwords do not match</Err1>
701 <ResponseCount>1</ResponseCount>
704 <ResponseNumber>304156</ResponseNumber>
705 <ResponseString>Validation error; invalid ; password</ResponseString>
708 <MinPeriod>1</MinPeriod>
709 <MaxPeriod>10</MaxPeriod>
710 <Server>Reseller1</Server>
712 <IsLockable>True</IsLockable>
713 <IsRealTimeTLD>True</IsRealTimeTLD>
714 <TimeDifference>+03.00</TimeDifference>
715 8<ExecTime>0.0625</ExecTime>
717 <debug><![CDATA[]]></debug>
718 </interface-response>"
721 static void update_namecheap(void)
730 sprintf(query
, "/update?host=%s&domain=%s&password=%s",
731 get_option_required("host"), get_option("user") ? : get_option_required("domain"), get_option_required("pass"));
734 append_addr_option(query
, "&ip=%s");
736 r
= wget(0, 0, "dynamicdns.park-your-domain.com", query
, NULL
, 0, &body
);
738 if (strstr(body
, "<ErrCount>0<") != NULL
) {
741 if ((p
= strstr(body
, "<Err1>")) != NULL
) {
743 if ((q
= strstr(p
, "</")) != NULL
) {
745 if ((q
- p
) >= 64) p
[64] = 0;
749 error(M_UNKNOWN_RESPONSE__D
, -1);
752 error(M_UNKNOWN_ERROR__D
, r
);
759 http://www.enom.com/help/faq_dynamicdns.asp
765 ;Machine is Reseller5<br>
778 TimeDifference=+08.00
783 ;Machine is Reseller4<br>
787 Err1=Passwords do not match
789 ResponseNumber1=304156
790 ResponseString1=Validation error; invalid ; password
797 TimeDifference=+08.00
802 static void update_enom(void)
810 // http://dynamic.name-services.com/interface.asp?Command=SetDNSHost&HostName=test&Zone=test.com&Address=1.2.3.4&DomainPassword=password
813 sprintf(query
, "/interface.asp?Command=SetDNSHost&HostName=%s&Zone=%s&DomainPassword=%s",
814 get_option_required("host"), get_option("user") ? : get_option_required("domain"), get_option_required("pass"));
817 append_addr_option(query
, "&Address=%s");
819 r
= wget(0, 0, "dynamic.name-services.com", query
, NULL
, 0, &body
);
821 if (strstr(body
, "ErrCount=0") != NULL
) {
824 if ((p
= strstr(body
, "Err1=")) != NULL
) {
826 if ((q
= strchr(p
, '\n')) != NULL
) {
828 if ((q
- p
) >= 64) p
[64] = 0;
829 if ((q
= strchr(p
, '\r')) != NULL
) *q
= 0;
833 error(M_UNKNOWN_RESPONSE__D
, -1);
836 error(M_UNKNOWN_ERROR__D
, r
);
846 http://www.dnsexit.com/Direct.sv?cmd=ipClients
857 11=fail to find foo.bar.com"
860 4=Update too often. Please wait at least 8 minutes since the last update"
862 " HTTP/1.1 200 OK" <-- extra in body?
865 static void update_dnsexit(void)
872 sprintf(query
, "/RemoteUpdate.sv?action=edit&login=%s&password=%s&host=%s",
873 get_option_required("user"), get_option_required("pass"), get_option_required("host"));
876 append_addr_option(query
, "&myip=%s");
878 r
= wget(0, 0, "www.dnsexit.com", query
, NULL
, 0, &body
);
882 if ((strstr(body
, "0=Success") != NULL
) || (strstr(body
, "1=IP") != NULL
)) {
885 if ((strstr(body
, "2=Invalid") != NULL
) || (strstr(body
, "3=User") != NULL
)) {
886 error(M_INVALID_AUTH
);
888 if ((strstr(body
, "10=Host") != NULL
) || (strstr(body
, "11=fail") != NULL
)) {
889 error(M_INVALID_HOST
);
891 if (strstr(body
, "4=Update") != NULL
) {
895 error(M_UNKNOWN_RESPONSE__D
, -1);
898 error(M_UNKNOWN_ERROR__D
, r
);
910 mytest.testdomain.com:4
914 static void update_noip(void)
922 sprintf(query, "/dns?username=%s&password=%s&",
923 get_option_required("user"), get_option_required("pass"));
926 if (((c = get_option("group")) != NULL) && (*c)) {
927 sprintf(query + strlen(query), "group=%s", c);
930 sprintf(query + strlen(query), "hostname=%s", get_option_required("host"));
934 append_addr_option(query, "&ip=%s");
936 r = wget(0, 0, "dynupdate.no-ip.com", query, NULL, 0, &body);
938 if ((c = strchr(body, ':')) != NULL) {
942 case 0: // host same addr
943 while (*c == ' ') ++c;
945 error(M_UNKNOWN_RESPONSE__D, -1);
949 case 12: // group same addr
950 case 1: // host updated
951 case 11: // group updated
954 case 2: // invalid hostname
955 case 10: // invalid group
956 case 6: // account disabled
957 case 8: // disabled hostname
958 error(M_INVALID_HOST);
960 case 3: // invalid password
961 case 4: // invalid username
962 error(M_INVALID_AUTH);
967 case 7: // invalid IP supplied
968 case 99: // client banned
969 case 100: // invalid parameter
970 case 9: // redirect type
972 error(M_INVALID_PARAM__D, r);
975 error(M_UNKNOWN_RESPONSE__D, r);
984 error(M_UNKNOWN_ERROR__D, r);
993 http://www.no-ip.com/hostactive.php?host=<host>&domain=<dom>
996 static void update_noip_refresh(void)
1002 strlcpy(host
, get_option_required("host"), sizeof(host
));
1003 if ((domain
= strchr(host
, '.')) != NULL
) {
1007 sprintf(query
, "/hostactive.php?host=%s", host
);
1008 if (domain
) sprintf(query
+ strlen(query
), "&domain=%s", domain
);
1010 wget(0, 1, "www.no-ip.com", query
, NULL
, 0, NULL
);
1019 http://www.ieserver.net/tools.html
1023 http://ieserver.net/cgi-bin/dip.cgi?username=XXX&domain=XXX&password=XXX&updatehost=1
1026 domain = dip.jp, fam.cx, etc.
1030 static void update_ieserver(void)
1038 sprintf(query
, "/cgi-bin/dip.cgi?username=%s&domain=%s&password=%s&updatehost=1",
1039 get_option_required("user"), get_option_required("host"), get_option_required("pass"));
1041 r
= wget(0, 0, "ieserver.net", query
, NULL
, 0, &body
);
1043 if (strstr(body
, "<title>Error") != NULL
) {
1045 // <p>yuuzaa na mata pasuwoodo (EUC-JP)
1046 if ((p
= strstr(body
, "<p>\xA5\xE6\xA1\xBC\xA5\xB6\xA1\xBC")) != NULL
) { // <p>user
1047 error(M_INVALID_AUTH
);
1050 error(M_UNKNOWN_RESPONSE__D
, -1);
1056 error(M_UNKNOWN_ERROR__D
, r
);
1064 http://www.dyns.cx/documentation/technical/protocol/v1.1.php
1071 401 testuser not authenticated"
1075 static void update_dyns(void)
1083 sprintf(query
, "/postscript011.php?username=%s&password=%s&host=%s",
1084 get_option_required("user"), get_option_required("pass"), get_option_required("host"));
1087 append_addr_option(query
, "&ip=%s");
1090 sprintf(query
+ strlen(query
), "&devel=1");
1093 r
= wget(0, 0, "www.dyns.net", query
, NULL
, 0, &body
);
1095 while ((*body
== ' ') || (*body
== '\r') || (*body
== '\n')) {
1098 switch (r
= atoi(body
)) {
1103 error(M_INVALID_PARAM__D
, r
);
1106 error(M_INVALID_AUTH
);
1112 error(M_INVALID_HOST
);
1116 error(M_UNKNOWN_RESPONSE__D
, r
);
1119 error(M_UNKNOWN_ERROR__D
, r
);
1134 <td valign="top"><font size="4">Congratulations!
1135 You've successfully signed on with TZO.<br>
1136 </font><h1 align="center"><font size="2"
1137 face="Verdana">Here's information about your
1140 <h3 align="center"><font face="Verdana"><p>TZO Name: foobartest.tzo.com<br>
1141 IP Address: 1.2.3.4<br>
1142 Expiration: 2007-01-01 1:01:01</p>
1148 Invalid hostname or password:
1152 <td valign="top"><font size="4">Congratulations!
1153 You've successfully signed on with TZO.<br>
1154 </font><h1 align="center"><font size="2"
1155 face="Verdana">Here's information about your
1158 <h3 align="center"><font face="Verdana"><p>Error=bad authentication
1165 face="Verdana">Here's information about your
1168 <h3 align="center"><font face="Verdana"><p>Error=Try again later. Please wait at least 1 minute before any additional requests.1.2.3.4
1174 static void update_tzo(void)
1182 sprintf(query
, "/webclient/signedon.html?TZOName=%s&Email=%s&TZOKey=%s",
1183 get_option_required("host"), get_option_required("user"), get_option_required("pass"));
1186 append_addr_option(query
, "&IPAddress=%s");
1188 r
= wget(0, 0, "cgi.tzo.com", query
, NULL
, 0, &body
);
1190 if ((p
= strstr(body
, "Error=")) != NULL
) {
1192 if (strncmp(p
, "bad auth", 8) == 0) {
1193 error(M_INVALID_AUTH
);
1195 if (strncmp(p
, "Try again", 9) == 0) {
1198 error(M_UNKNOWN_ERROR__D
, -1);
1203 error(M_UNKNOWN_ERROR__D
, r
);
1212 http://www.zoneedit.com/doc/dynamic.html
1218 <SUCCESS CODE="200" TEXT="Update succeeded." ZONE="test123.com" HOST="www.test123.com" IP="1.2.3.4">"
1220 "<ERROR CODE="707" TEXT="Duplicate updates for the same host/ip, adjust client settings" ZONE="testexamplesite4321.com" HOST="test.testexamplesite4321.com">"
1222 "HTTP/1.1 401 Authorization Required
1224 <title>Authentication Failed </title>"
1226 ERROR CODE="[701-799]" TEXT="Description of the error" ZONE="Zone that Failed"
1227 ERROR CODE="702" TEXT="Update failed." ZONE="%zone%"
1228 ERROR CODE="703" TEXT="one of either parameters 'zones' or 'host' are required."
1229 ERROR CODE="705" TEXT="Zone cannot be empty" ZONE="%zone%"
1230 ERROR CODE="707" TEXT="Duplicate updates for the same host/ip, adjust client settings" ZONE="%zone%"
1231 ERROR CODE="707" TEXT="Too frequent updates for the same host, adjust client settings" ZONE="%zone%"
1232 ERROR CODE="704" TEXT="Zone must be a valid 'dotted' internet name." ZONE="%zone%"
1233 ERROR CODE="701" TEXT="Zone is not set up in this account." ZONE="%zone%"
1234 ERROR CODE="708" TEXT="Login/authorization error"
1235 SUCCESS CODE="[200-201]" TEXT="Description of the success" ZONE="Zone that Succeeded"
1236 SUCCESS CODE="200" TEXT="Update succeeded." ZONE="%zone%" IP="%dnsto%"
1237 SUCCESS CODE="201" TEXT="No records need updating." ZONE="%zone%"
1240 static void update_zoneedit(int ssl
)
1248 sprintf(query
, "/auth/dynamic.html?host=%s", get_option_required("host"));
1251 append_addr_option(query
, "&dnsto=%s");
1253 r
= wget(ssl
, 0, "dynamic.zoneedit.com", query
, NULL
, 1, &body
);
1256 if (strstr(body
, "<SUCCESS CODE")) {
1259 if ((c
= strstr(body
, "<ERROR CODE=\"")) != NULL
) {
1262 case 701: // invalid "zone"
1263 error(M_INVALID_HOST
);
1265 case 707: // update is the same ip address? / too frequent updates
1266 if (strstr(c
, "Duplicate") != NULL
) success();
1267 else error(M_TOOSOON
);
1269 case 708: // authorization error
1270 error(M_INVALID_AUTH
);
1273 error(M_UNKNOWN_RESPONSE__D
, r
);
1275 error(M_UNKNOWN_RESPONSE__D
, -1);
1278 error(M_INVALID_AUTH
);
1282 error(M_UNKNOWN_ERROR__D
, r
);
1293 http://freedns.afraid.org/dynamic/update.php?XXXXXXXXXXYYYYYYYYYYYZZZZZZZ1111222
1298 ERROR: Address 1.2.3.4 has not changed."
1300 "Updated 1 host(s) foobar.mooo.com to 1.2.3.4 in 1.234 seconds"
1302 "ERROR: Missing S/key and DataID, check your update URL."
1304 "fail, make sure you own this record, and the address does not already equal 1.2.3.4"
1309 static void update_afraid(void)
1316 sprintf(query
, "/dynamic/update.php?%s", get_option_required("ahash"));
1318 r
= wget(0, 0, "freedns.afraid.org", query
, NULL
, 0, &body
);
1320 if ((strstr(body
, "ERROR") != NULL
) || (strstr(body
, "fail") != NULL
)) {
1321 if (strstr(body
, "has not changed") != NULL
) {
1324 error(M_INVALID_AUTH
);
1326 else if ((strstr(body
, "Updated") != NULL
) && (strstr(body
, "host") != NULL
)) {
1330 error(M_UNKNOWN_RESPONSE__D
, -1);
1334 error(M_UNKNOWN_ERROR__D
, r
);
1343 X-Powered-By: PHP/5.0.1-dev
1345 Content-Type: text/html; charset=utf-8
1347 Authentication given
1348 Authentication failed: Bad Username/password
1352 static void update_everydns(void)
1359 strcpy(query
, "/index.php?ver=0.1");
1360 append_addr_option(query
, "&ip=%s");
1362 p
= get_option("host");
1363 if (p
) sprintf(query
+ strlen(query
), "&domain=%s", p
);
1365 r
= wget(0, 0, "dyn.everydns.net", query
, NULL
, 1, &body
);
1367 if ((p
= strstr(body
, "Exit code:")) != NULL
) {
1374 error(M_INVALID_AUTH
);
1377 error(M_UNKNOWN_RESPONSE__D
, r
);
1381 error(M_UNKNOWN_RESPONSE__D
, -1);
1384 error(M_UNKNOWN_ERROR__D
, r
);
1391 http://www.minidns.net/areg.php?opcode=ADD&host=bar.minidns.net&username=foo&password=topsecret&ip=1.2.3.4
1395 "okay. BAR.MINIDNS.NET mapped to 1.2.3.4."
1396 "auth_fail. Incorrect username/password/hostname."
1397 "auth_fail. Host name format error."
1400 static void update_minidns(void)
1407 sprintf(query
, "/areg.php?opcode=ADD&host=%s&username=%s&password=%s",
1408 get_option_required("host"),
1409 get_option_required("user"),
1410 get_option_required("pass"));
1413 append_addr_option(query
, "&ip=%s");
1415 r
= wget(0, 0, "www.minidns.net", query
, NULL
, 1, &body
);
1417 if (strstr(body
, "okay.") != NULL
) {
1420 else if (strstr(body
, "Host name format error") != NULL
) {
1421 error(M_INVALID_HOST
);
1423 else if (strstr(body
, "auth_fail") != NULL
) {
1424 error(M_INVALID_AUTH
);
1427 error(M_UNKNOWN_RESPONSE__D
, -1);
1431 error(M_UNKNOWN_ERROR__D
, r
);
1438 http://www.editdns.net/
1444 http://DynDNS.EditDNS.net/api/dynLinux.php?p=XXX&r=XXX
1450 static void update_editdns(void)
1457 sprintf(query
, "/api/dynLinux.php?p=%s&r=%s",
1458 get_option_required("pass"), get_option_required("host"));
1460 r
= wget(0, 1, "DynDNS.EditDNS.net", query
, NULL
, 0, &body
);
1462 if (strstr(body
, "Record has been updated") != NULL
) {
1465 if (strstr(body
, "Record already exists") != NULL
) {
1468 else if (strstr(body
, "Invalid Username") != NULL
) {
1469 error(M_INVALID_AUTH
);
1471 else if (strstr(body
, "Invalid DynRecord") != NULL
) {
1472 error(M_INVALID_HOST
);
1479 error(M_UNKNOWN_ERROR__D
, r
);
1486 HE.net IPv6 TunnelBroker
1487 https://ipv4.tunnelbroker.net/ipv4_end.php?ip=$IPV4ADDR&pass=$MD5PASS&apikey=$USERID&tid=$TUNNELID
1495 +OK: Tunnel endpoint updated to: XXX.XXX.XXX.XXX
1496 -ERROR: This tunnel is already associated with this IP address. Please try and limit your updates to IP changes.
1498 -ERROR: Tunnel not found
1499 -ERROR: Invalid API key or password
1500 -ERROR: IP is not in a valid format
1501 -ERROR: Missing parameter(s).
1502 -ERROR: IP is not ICMP pingable. Please make sure ICMP is not blocked. If you are blocking ICMP, please allow 66.220.2.74 through your firewall.
1503 -ERROR: IP is blocked. (RFC1918 Private Address Space)
1506 static void update_heipv6tb(void)
1510 const char *serr
= "-ERROR: ";
1514 sprintf(query
, "/ipv4_end.php?pass=%s&apikey=%s&tid=%s",
1515 md5_string(get_option_required("pass")),
1516 get_option_required("user"),
1517 get_option_required("host"));
1520 append_addr_option(query
, "&ip=%s");
1522 r
= wget(1, 0, "ipv4.tunnelbroker.net", query
, NULL
, 0, &body
);
1524 if ((strstr(body
, "endpoint updated to") != NULL
) || (strstr(body
, "is already associated") != NULL
)) {
1527 if (strstr(body
, "Invalid API key or password") != NULL
) {
1528 error(M_INVALID_AUTH
);
1530 if (strstr(body
, "Tunnel not found") != NULL
) {
1531 error(M_INVALID_PARAM__S
, "Tunnel ID");
1533 if (strstr(body
, "IP is not in a valid format") != NULL
) {
1534 error(M_INVALID_PARAM__S
, "IPv4 endpoint");
1537 error(M_UNKNOWN_RESPONSE__D
, -1);
1540 error(M_UNKNOWN_ERROR__D
, r
);
1550 static void update_wget(void)
1564 // http://user:pass@domain:port/path?query
1565 // https://user:pass@domain:port/path?query
1567 strcpy(url
, get_option_required("url"));
1570 if (strncasecmp(url
, "https://", 8) == 0) {
1574 else if (strncasecmp(url
, "http://", 7) != 0) {
1575 error(M_INVALID_PARAM__S
, "url");
1578 if ((p
= strchr(host
, '/')) == NULL
) {
1579 error(M_INVALID_PARAM__S
, "url");
1584 if ((c
= strstr(path
, "@IP")) != NULL
) {
1586 strcpy(c
, get_address(1));
1590 if ((c
= strrchr(host
, '@')) != NULL
) {
1592 s
[base64_encode(host
, s
, c
- host
)] = 0;
1593 sprintf(he
, "Authorization: Basic %s\r\n", s
);
1601 r
= wget(https
, 1, host
, path
, header
, 0, &body
);
1604 case 302: // redirect -- assume ok
1605 if (body
&& *body
) success_msg((char *)body
);
1609 error(M_INVALID_AUTH
);
1613 error(M_UNKNOWN_ERROR__D
, r
);
1616 // -----------------------------------------------------------------------------
1620 static void check_cookie(void)
1626 if (((c
= get_option("cookie")) == NULL
) || (!read_tmaddr(c
, &tm
, addr
))) {
1627 _dprintf("%s: no cookie\n", __FUNCTION__
);
1632 if ((c
= get_address(0)) == NULL
) {
1633 _dprintf("%s: no address specified\n", __FUNCTION__
);
1636 if (strcmp(c
, addr
) != 0) {
1637 _dprintf("%s: address is different (%s != %s)\n", __FUNCTION__
, c
, addr
);
1646 if ((now
< Y2K
) || (now
< tm
)) {
1647 _dprintf("%s: time rolled back (now=%ld, tm=%ld)\n", __FUNCTION__
, now
, tm
);
1652 _dprintf("%s: addr=%s tm=%ld (relative)\n", __FUNCTION__
, addr
, tm
);
1654 if ((c
= get_option("maxtime")) != NULL
) {
1655 u
= strtol(c
, NULL
, 0);
1658 _dprintf("%s: %s expired (%ld > %ld)\n", __FUNCTION__
, addr
, tm
, u
);
1661 _dprintf("%s: maxtime=%ld tm=%ld\n", __FUNCTION__
, u
, tm
);
1668 if ((c
= get_option("mintime")) != NULL
) {
1669 u
= strtol(c
, NULL
, 0);
1670 if ((u
> 0) && (tm
< u
)) {
1671 _dprintf("%s: %s recently updated (%ld < %ld)\n", __FUNCTION__
, addr
, tm
, u
);
1684 static void save_cookie(void)
1693 _dprintf("%s: no time", __FUNCTION__
);
1697 if ((cookie
= get_option("cookie")) == NULL
) {
1698 _dprintf("%s: no cookie\n", __FUNCTION__
);
1702 if ((c
= get_address(0)) == NULL
) {
1703 _dprintf("%s: no address specified\n", __FUNCTION__
);
1707 sprintf(s
, "%ld,%s", now
, c
);
1708 f_write_string(cookie
, s
, FW_NEWLINE
, 0);
1710 _dprintf("%s: cookie=%s\n", __FUNCTION__
, s
);
1713 int main(int argc
, char *argv
[])
1720 printf("MDU\nCopyright (C) 2007-2009 Jonathan Zarate\n\n");
1721 _dprintf("DEBUG\n");
1723 if ((blob
= malloc(BLOB_SIZE
)) == NULL
) {
1727 mkdir("/var/lib/mdu", 0700);
1728 chdir("/var/lib/mdu");
1730 if ((p = get_dump_name()) != NULL) {
1736 p
= get_option_required("service");
1737 if (strcmp(p
, "dua") == 0) {
1738 update_dua("dyndns", 0, NULL
, NULL
, 1);
1740 else if (strcmp(p
, "duas") == 0) {
1741 update_dua("dyndns", 1, NULL
, NULL
, 1);
1743 else if (strcmp(p
, "dyndns") == 0) {
1744 // test ok 9/14 -- zzz
1745 update_dua("dyndns", 0, "members.dyndns.org", "/nic/update", 1);
1747 else if (strcmp(p
, "dyndns-static") == 0) {
1748 // test ok 9/14 -- zzz
1749 update_dua("statdns", 0, "members.dyndns.org", "/nic/update", 1);
1751 else if (strcmp(p
, "dyndns-custom") == 0) {
1752 // test ok 9/14 -- zzz
1753 update_dua("custom", 0, "members.dyndns.org", "/nic/update", 1);
1755 else if (strcmp(p
, "sdyndns") == 0) {
1756 update_dua("dyndns", 1, "members.dyndns.org", "/nic/update", 1);
1758 else if (strcmp(p
, "sdyndns-static") == 0) {
1759 update_dua("statdns", 1, "members.dyndns.org", "/nic/update", 1);
1761 else if (strcmp(p
, "sdyndns-custom") == 0) {
1762 update_dua("custom", 1, "members.dyndns.org", "/nic/update", 1);
1764 else if (strcmp(p
, "easydns") == 0) {
1765 // no account, test output ok, test 401 error parse ok 9/15 -- zzz
1766 update_dua(NULL
, 0, "members.easydns.com", "/dyn/dyndns.php", 1);
1768 else if (strcmp(p
, "seasydns") == 0) {
1769 update_dua(NULL
, 1, "members.easydns.com", "/dyn/dyndns.php", 1);
1771 else if (strcmp(p
, "3322") == 0) {
1772 // no account, test output ok, test 401 error parse ok 9/16 -- zzz
1773 update_dua(NULL
, 0, "members.3322.org", "/dyndns/update", 1);
1775 else if (strcmp(p
, "3322-static") == 0) {
1776 // no account, test output ok, test 401 error parse ok 9/16 -- zzz
1777 update_dua("statdns", 0, "members.3322.org", "/dyndns/update", 1);
1779 else if (strcmp(p
, "opendns") == 0) {
1780 // test ok 9/15 -- zzz
1781 update_dua(NULL
, 1, "updates.opendns.com", "/nic/update", 0);
1783 else if (strcmp(p
, "dnsomatic") == 0) {
1784 // test ok 12/02 -- zzz
1785 update_dua(NULL
, 1, "updates.dnsomatic.com", "/nic/update", 0);
1787 else if (strcmp(p
, "noip") == 0) {
1788 update_dua(NULL
, 0, "dynupdate.no-ip.com", "/nic/update", 1);
1791 else if (strcmp(p
, "namecheap") == 0) {
1792 // test ok 9/14 -- zzz
1795 else if (strcmp(p
, "enom") == 0) {
1796 // no account, test output ok, 12/03 -- zzz
1799 else if (strcmp(p
, "dnsexit") == 0) {
1800 // test ok 9/14 -- zzz
1803 else if (strcmp(p
, "ieserver") == 0) {
1804 // test ok 9/14 -- zzz
1807 else if (strcmp(p
, "dyns") == 0) {
1808 // no account, test output ok, test 401 error parse ok 9/15 -- zzz
1811 else if (strcmp(p
, "tzo") == 0) {
1812 // test ok 9/15 -- zzz
1815 else if (strcmp(p
, "zoneedit") == 0) {
1816 // test ok 9/16 -- zzz
1819 else if (strcmp(p
, "szoneedit") == 0) {
1822 else if (strcmp(p
, "afraid") == 0) {
1823 // test ok 9/16 -- zzz
1826 else if (strcmp(p
, "everydns") == 0) {
1830 else if (strcmp(p
, "editdns") == 0) {
1833 else if (strcmp(p
, "minidns") == 0) {
1836 else if (strcmp(p
, "heipv6tb") == 0) {
1839 else if (strcmp(p
, "pairnic") == 0) {
1840 // pairNIC uses the same API as DynDNS
1841 update_dua("pairnic", 0, "dynamic.pairnic.com", "/nic/update", 1);
1843 else if (strcmp(p
, "spairnic") == 0) {
1844 // pairNIC uses the same API as DynDNS
1845 update_dua("pairnic", 1, "dynamic.pairnic.com", "/nic/update", 1);
1847 else if (strcmp(p
, "ovh") == 0) {
1848 // OVH uses the same API as DynDNS
1849 update_dua("dyndns", 0, "www.ovh.com", "/nic/update", 1);
1851 else if (strcmp(p
, "sovh") == 0) {
1852 // OVH uses the same API as DynDNS
1853 update_dua("dyndns", 1, "www.ovh.com", "/nic/update", 1);
1855 else if (strcmp(p
, "schangeip") == 0) {
1856 // ChangeIP uses the same API as DynDNS
1857 update_dua("dyndns", 1, "nic.changeip.com", "/nic/update", 1);
1859 else if ((strcmp(p
, "wget") == 0) || (strcmp(p
, "custom") == 0)) {
1860 // test ok 9/15 -- zzz
1864 error("Unknown service");