1 /* -- updatedd: libovh.c --
2  *
3  * Copyright (C) 2002, 2003, 2004, 2005 Philipp Benner
4  *
5  * This file is part of UpdateDD - http://updatedd.philipp-benner.de.
6  *
7  * UpdateDD is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * any later version.
11  *
12  * UpdateDD is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with UpdateDD; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <ctype.h>
28 #include <unistd.h>
29 #include <getopt.h>
30 
31 #include <base64encode.h>
32 #include <get_connection.h>
33 #include <updatedd-exception.h>
34 #include <ret_codes.h>
35 #include <version.h>
36 
37 #include "libovh.h"
38 
39 static void
print_usage(char * pname,FILE * fp)40 print_usage(char *pname, FILE *fp)
41 {
42 	(void)fprintf(fp,
43 		      "\nUsage: %s [...] %s -- [OPTION]... [USERNAME:PASSWORD] HOSTNAME\n\n",
44 		      pname, COLORED("ovh"));
45 	(void)fprintf(fp,
46 		      "For security reasons use the environment variable LOGIN instead of\n"
47 		      "passing the login information directly.\n\n"
48 		      "Options:\n"
49 		      "   -4    --ipv4 <address>        ip address version 4\n"
50 		      "         --help                  print help and exit\n"
51 		      "         --version               display version information and exit\n\n"
52 		      "Report bugs to <"EMAIL">.\n\n");
53 
54 	return;
55 }
56 
57 static void
print_version(FILE * fp)58 print_version(FILE *fp)
59 {
60 
61 	(void)fprintf(fp,
62                       "\n" PNAME " plugin for ovh.com version " VERSION ",\n"
63                       "Copyright (C) 2005 Philipp Benner.\n"
64                       HOMEPAGE "\n\n"
65 
66                       "This is free software, and you are welcome to redistribute it\n"
67                       "under certain conditions; see the source for copying conditions.\n"
68                       "There is NO warranty; not even for MERCHANTABILITY or FITNESS\n"
69                       "FOR A PARTICULAR PURPOSE.\n\n");
70 
71 	return;
72 
73 }
74 
75 static void
ret_msg(int mode,const char * fmt,...)76 ret_msg(int mode, const char *fmt, ...)
77 {
78 
79 	va_list az;
80 
81 	va_start(az, fmt);
82 	(void)vs_warn(ret_msg_buf, BUFSIZE, mode, fmt, az);
83 	va_end(az);
84 
85 	return;
86 
87 }
88 
89 int
dyndns(int argc,char * argv[])90 dyndns(int argc, char *argv[])
91 {
92 
93 	struct arguments args;
94 	const char *ptr;
95 	int s, ret;
96 
97 	(void)memset(&args, 0, sizeof(struct arguments));
98 
99 	if(get_flags(&args, argc, argv) != RET_OK) {
100 		return RET_WRONG_USAGE;
101 	}
102 
103 	s = get_connection(DYNDNSHOST, PORT, &ptr);
104 	if(s == -1) {
105 		ret_msg(HERR, "%s: %s", ptr, DYNDNSHOST);
106 		ret = RET_WARNING;
107 	} else {
108 		ret = update_dyndns(s, &args);
109 		if(ret == RET_OK) {
110 			ret = check_server_msg(s, args.hostname);
111 		}
112 		(void)close(s);
113 	}
114 
115 	return ret;
116 
117 }
118 
119 static int
get_flags(struct arguments * args,int argc,char * argv[])120 get_flags(struct arguments *args, int argc, char *argv[])
121 {
122 
123 	int c;
124 
125 	for(;;) {
126 
127 		int option_index = 0;
128 		static struct option long_options[] = {
129 			{ "ipv4",		1, 0, '4' },
130 			{ "help",		0, 0, 'h' },
131 			{ "version",		0, 0, 'v' },
132 			{ NULL,			0, 0, 0   }
133 		};
134 
135 		c = getopt_long(argc, argv, "4:",
136 				long_options, &option_index);
137 
138 		if(c == -1) break;
139 
140 		switch(c) {
141 		case '4':
142 			args->ipv4 = optarg;
143 			break;
144 		case 'h':
145 			print_usage(argv[ARGV_PNAME], stdout);
146 			exit(EXIT_SUCCESS);
147 		case 'v':
148 			print_version(stdout);
149 			exit(EXIT_SUCCESS);
150 		}
151 	}
152 
153 	switch(argc-optind) {
154         default:
155 		ret_msg(NONE, "wrong usage");
156 		return RET_WRONG_USAGE;
157 
158         case 2:
159 		args->login = getenv("LOGIN");
160 		if(args->login == NULL) {
161 			ret_msg(NONE,
162 				"environment variable LOGIN is empty");
163 			return RET_WRONG_USAGE;
164 		}
165 		break;
166         case 3:
167 		args->login = argv[ARGV_LOGIN];
168 	}
169 	args->hostname = argv[ARGV_HOSTNAME];
170 
171 	return RET_OK;
172 
173 }
174 
175 #define BUFLEN		4096
176 #define BUFFREE(name)	BUFLEN - strlen(name)
177 
178 static int
update_dyndns(int s,struct arguments * args)179 update_dyndns(int s, struct arguments *args)
180 {
181 
182 	char *b64user;
183 	char message[BUFLEN];
184 
185 	if(strlen(args->login) > 128) {
186 		ret_msg(NONE, "username is too long");
187 		return RET_ERROR;
188 	}
189 	b64user = (char *)malloc((2 * strlen(args->login) + 1));
190 	if(b64user == NULL) {
191 		ret_msg(PERR, "malloc() failed");
192 		return RET_WARNING;
193 	}
194 	(void)memset(b64user, 0, 2 * strlen(args->login) + 1);
195 
196 	base64encode(args->login, b64user);
197 	(void)snprintf(message, BUFLEN,
198                        "GET /nic/update?system=dyndns&hostname=%s",
199                        args->hostname);
200 
201 	if(args->ipv4) {
202 		(void)strncat(message, "&myip=", BUFFREE(message));
203 		(void)strncat(message, args->ipv4, BUFFREE(message));
204 	}
205 
206 	{
207 		char buffer[1024];
208 
209 		(void)snprintf(buffer, 1024,
210                                " HTTP/1.1\r\n"
211                                "Host: %s\r\n"
212                                "Authorization: Basic %s\r\n"
213                                "User-Agent: %s %s - %s\r\n"
214                                "Connection: close\r\n"
215                                "Pragma: no-cache\r\n\r\n",
216                                DYNDNSHOST, b64user, PNAME, VERSION, HOMEPAGE);
217 		(void)strncat(message, buffer, BUFLEN - 1 - strlen(message));
218 	}
219 	print_debug("\n\nMessage:"
220 		    "\n--------------------------------------\n"
221 		    "%s--------------------------------------\n\n",
222                     message);
223 
224 	if(write(s, message, strlen(message)) == -1) {
225 		ret_msg(PERR, "write() failed");
226 		return RET_WARNING;
227 	}
228 
229 	free(b64user);
230 	return RET_OK;
231 
232 }
233 
234 static int
check_server_msg(int s,char * hostname)235 check_server_msg(int s, char *hostname)
236 {
237 
238 	int n;
239 	char server_msg[BUFSIZE], *ptr;
240 
241 	/* get server_msg */
242 	(void)memset(server_msg, 0, sizeof(server_msg));
243 	if(read(s, server_msg, sizeof(server_msg) - 1) < 0) {
244 		ret_msg(PERR, "read() failed");
245 		return RET_WARNING;
246 	}
247 
248 	print_debug("\n\nServer message:"
249 		    "\n--------------------------------------\n"
250 		    "%s--------------------------------------\n\n",
251 		    server_msg);
252 
253 	if(strstr(server_msg, "HTTP/1.1 200 OK") ||
254 	   strstr(server_msg, "HTTP/1.0 200 OK")) {
255 
256 		(void)strtok(server_msg, "\n");
257 		while((ptr = strtok(NULL, "\n")) != NULL) {
258 			for(n=0; return_codes[n].code != NULL; n++) {
259 				if(strstr(ptr, return_codes[n].code)) {
260 					ret_msg(NONE, "%s: %s",
261 						hostname, return_codes[n].message);
262 					if(return_codes[n].error == 1) {
263 						return RET_ERROR;
264 					} else {
265 						return RET_OK;
266 					}
267 				}
268 			}
269 		}
270 	} else if(strstr(server_msg, "401 Authorization Required")) {
271 		ret_msg(NONE, "wrong username or password");
272 	} else {
273 		ret_msg(NONE, "Internal Server Error");
274 	}
275 
276 	return RET_ERROR;
277 }
278