67 |
67 |
68 #ifdef HAVE_UNISTD_H |
68 #ifdef HAVE_UNISTD_H |
69 #include <unistd.h> |
69 #include <unistd.h> |
70 #endif |
70 #endif |
71 |
71 |
72 |
|
73 /* we include this here to get the G_OS_* defines */ |
72 /* we include this here to get the G_OS_* defines */ |
74 #include <glib.h> |
73 #include <glib.h> |
75 #include <gst/gst.h> |
74 #include <gst/gst.h> |
76 |
75 |
77 #ifdef G_OS_WIN32 |
76 #ifdef G_OS_WIN32 |
|
77 /* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later. |
|
78 * minwg32 headers check WINVER before allowing the use of these */ |
|
79 #ifndef WINVER |
|
80 #define WINVER 0x0501 |
|
81 #endif |
78 #include <winsock2.h> |
82 #include <winsock2.h> |
|
83 #include <ws2tcpip.h> |
79 #define EINPROGRESS WSAEINPROGRESS |
84 #define EINPROGRESS WSAEINPROGRESS |
80 #else |
85 #else |
81 #include <sys/ioctl.h> |
86 #include <sys/ioctl.h> |
82 #include <netdb.h> |
87 #include <netdb.h> |
83 #include <sys/socket.h> |
88 #include <sys/socket.h> |
|
89 #include <fcntl.h> |
84 #include <netinet/in.h> |
90 #include <netinet/in.h> |
85 #include <arpa/inet.h> |
|
86 #include <fcntl.h> |
|
87 #endif |
91 #endif |
88 |
92 |
89 #ifdef HAVE_FIONREAD_IN_SYS_FILIO |
93 #ifdef HAVE_FIONREAD_IN_SYS_FILIO |
90 #include <sys/filio.h> |
94 #include <sys/filio.h> |
91 #endif |
95 #endif |
92 |
96 |
93 #include "gstrtspconnection.h" |
97 #include "gstrtspconnection.h" |
94 #include "gstrtspbase64.h" |
98 #include "gstrtspbase64.h" |
|
99 #include "md5.h" |
|
100 |
|
101 union gst_sockaddr |
|
102 { |
|
103 struct sockaddr sa; |
|
104 struct sockaddr_in sa_in; |
|
105 struct sockaddr_in6 sa_in6; |
|
106 struct sockaddr_storage sa_stor; |
|
107 }; |
|
108 |
|
109 typedef struct |
|
110 { |
|
111 gint state; |
|
112 guint save; |
|
113 guchar out[3]; /* the size must be evenly divisible by 3 */ |
|
114 guint cout; |
|
115 guint coutl; |
|
116 } DecodeCtx; |
|
117 |
|
118 static GstRTSPResult read_line (gint fd, guint8 * buffer, guint * idx, |
|
119 guint size, DecodeCtx * ctxp); |
|
120 static GstRTSPResult parse_key_value (guint8 * buffer, gchar * key, |
|
121 guint keysize, gchar ** value); |
|
122 static void parse_string (gchar * dest, gint size, gchar ** src); |
95 |
123 |
96 #ifdef G_OS_WIN32 |
124 #ifdef G_OS_WIN32 |
97 #define FIONREAD_TYPE gulong |
125 #define READ_SOCKET(fd, buf, len) recv (fd, (char *)buf, len, 0) |
98 #define IOCTL_SOCKET ioctlsocket |
126 #define WRITE_SOCKET(fd, buf, len) send (fd, (const char *)buf, len, 0) |
99 #define READ_SOCKET(fd, buf, len) recv (fd, buf, len, 0) |
127 #define SETSOCKOPT(sock, level, name, val, len) setsockopt (sock, level, name, (const char *)val, len) |
100 #define WRITE_SOCKET(fd, buf, len) send (fd, buf, len, 0) |
|
101 #define CLOSE_SOCKET(sock) closesocket (sock) |
128 #define CLOSE_SOCKET(sock) closesocket (sock) |
102 #define ERRNO_IS_NOT_EAGAIN (WSAGetLastError () != WSAEWOULDBLOCK) |
129 #define ERRNO_IS_EAGAIN (WSAGetLastError () == WSAEWOULDBLOCK) |
103 #define ERRNO_IS_NOT_EINTR (WSAGetLastError () != WSAEINTR) |
130 #define ERRNO_IS_EINTR (WSAGetLastError () == WSAEINTR) |
104 /* According to Microsoft's connect() documentation this one returns |
131 /* According to Microsoft's connect() documentation this one returns |
105 * WSAEWOULDBLOCK and not WSAEINPROGRESS. */ |
132 * WSAEWOULDBLOCK and not WSAEINPROGRESS. */ |
106 #define ERRNO_IS_NOT_EINPROGRESS (WSAGetLastError () != WSAEWOULDBLOCK) |
133 #define ERRNO_IS_EINPROGRESS (WSAGetLastError () == WSAEWOULDBLOCK) |
107 #else |
134 #else |
108 #define FIONREAD_TYPE gint |
|
109 #define IOCTL_SOCKET ioctl |
|
110 #define READ_SOCKET(fd, buf, len) read (fd, buf, len) |
135 #define READ_SOCKET(fd, buf, len) read (fd, buf, len) |
111 #define WRITE_SOCKET(fd, buf, len) write (fd, buf, len) |
136 #define WRITE_SOCKET(fd, buf, len) write (fd, buf, len) |
|
137 #define SETSOCKOPT(sock, level, name, val, len) setsockopt (sock, level, name, val, len) |
112 #define CLOSE_SOCKET(sock) close (sock) |
138 #define CLOSE_SOCKET(sock) close (sock) |
113 #define ERRNO_IS_NOT_EAGAIN (errno != EAGAIN) |
139 #define ERRNO_IS_EAGAIN (errno == EAGAIN) |
114 #define ERRNO_IS_NOT_EINTR (errno != EINTR) |
140 #define ERRNO_IS_EINTR (errno == EINTR) |
115 #define ERRNO_IS_NOT_EINPROGRESS (errno != EINPROGRESS) |
141 #define ERRNO_IS_EINPROGRESS (errno == EINPROGRESS) |
116 #endif |
142 #endif |
117 |
143 |
118 #ifdef G_OS_WIN32 |
144 #define ADD_POLLFD(fdset, pfd, fd) \ |
119 static int |
145 G_STMT_START { \ |
120 inet_aton (const char *c, struct in_addr *paddr) |
146 (pfd)->fd = fd; \ |
121 { |
147 gst_poll_add_fd (fdset, pfd); \ |
122 /* note that inet_addr is deprecated on unix because |
148 } G_STMT_END |
123 * inet_addr returns -1 (INADDR_NONE) for the valid 255.255.255.255 |
149 |
124 * address. */ |
150 #define REMOVE_POLLFD(fdset, pfd) \ |
125 paddr->s_addr = inet_addr (c); |
151 G_STMT_START { \ |
126 |
152 if ((pfd)->fd != -1) { \ |
127 if (paddr->s_addr == INADDR_NONE) |
153 GST_DEBUG ("remove fd %d", (pfd)->fd); \ |
128 return 0; |
154 gst_poll_remove_fd (fdset, pfd); \ |
129 |
155 CLOSE_SOCKET ((pfd)->fd); \ |
130 return 1; |
156 (pfd)->fd = -1; \ |
131 } |
157 } \ |
132 #endif |
158 } G_STMT_END |
|
159 |
|
160 typedef enum |
|
161 { |
|
162 TUNNEL_STATE_NONE, |
|
163 TUNNEL_STATE_GET, |
|
164 TUNNEL_STATE_POST, |
|
165 TUNNEL_STATE_COMPLETE |
|
166 } GstRTSPTunnelState; |
|
167 |
|
168 #define TUNNELID_LEN 24 |
|
169 |
|
170 struct _GstRTSPConnection |
|
171 { |
|
172 /*< private > */ |
|
173 /* URL for the connection */ |
|
174 GstRTSPUrl *url; |
|
175 |
|
176 /* connection state */ |
|
177 GstPollFD fd0; |
|
178 GstPollFD fd1; |
|
179 |
|
180 GstPollFD *readfd; |
|
181 GstPollFD *writefd; |
|
182 |
|
183 gchar tunnelid[TUNNELID_LEN]; |
|
184 gboolean tunneled; |
|
185 GstRTSPTunnelState tstate; |
|
186 |
|
187 GstPoll *fdset; |
|
188 gchar *ip; |
|
189 |
|
190 /* Session state */ |
|
191 gint cseq; /* sequence number */ |
|
192 gchar session_id[512]; /* session id */ |
|
193 gint timeout; /* session timeout in seconds */ |
|
194 GTimer *timer; /* timeout timer */ |
|
195 |
|
196 /* Authentication */ |
|
197 GstRTSPAuthMethod auth_method; |
|
198 gchar *username; |
|
199 gchar *passwd; |
|
200 GHashTable *auth_params; |
|
201 |
|
202 DecodeCtx ctx; |
|
203 DecodeCtx *ctxp; |
|
204 |
|
205 gchar *proxy_host; |
|
206 guint proxy_port; |
|
207 }; |
|
208 |
|
209 enum |
|
210 { |
|
211 STATE_START = 0, |
|
212 STATE_DATA_HEADER, |
|
213 STATE_DATA_BODY, |
|
214 STATE_READ_LINES, |
|
215 STATE_END, |
|
216 STATE_LAST |
|
217 }; |
|
218 |
|
219 /* a structure for constructing RTSPMessages */ |
|
220 typedef struct |
|
221 { |
|
222 gint state; |
|
223 guint8 buffer[4096]; |
|
224 guint offset; |
|
225 |
|
226 guint line; |
|
227 guint8 *body_data; |
|
228 glong body_len; |
|
229 } GstRTSPBuilder; |
|
230 |
|
231 static void |
|
232 build_reset (GstRTSPBuilder * builder) |
|
233 { |
|
234 g_free (builder->body_data); |
|
235 memset (builder, 0, sizeof (GstRTSPBuilder)); |
|
236 } |
133 |
237 |
134 /** |
238 /** |
135 * gst_rtsp_connection_create: |
239 * gst_rtsp_connection_create: |
136 * @url: a #GstRTSPUrl |
240 * @url: a #GstRTSPUrl |
137 * @conn: a #GstRTSPConnection |
241 * @conn: storage for a #GstRTSPConnection |
138 * |
242 * |
139 * Create a newly allocated #GstRTSPConnection from @url and store it in @conn. |
243 * Create a newly allocated #GstRTSPConnection from @url and store it in @conn. |
140 * The connection will not yet attempt to connect to @url, use |
244 * The connection will not yet attempt to connect to @url, use |
141 * gst_rtsp_connection_connect(). |
245 * gst_rtsp_connection_connect(). |
142 * |
246 * |
|
247 * A copy of @url will be made. |
|
248 * |
143 * Returns: #GST_RTSP_OK when @conn contains a valid connection. |
249 * Returns: #GST_RTSP_OK when @conn contains a valid connection. |
144 */ |
250 */ |
145 GstRTSPResult |
251 GstRTSPResult |
146 gst_rtsp_connection_create (GstRTSPUrl * url, GstRTSPConnection ** conn) |
252 gst_rtsp_connection_create (const GstRTSPUrl * url, GstRTSPConnection ** conn) |
147 { |
253 { |
148 GstRTSPConnection *newconn; |
254 GstRTSPConnection *newconn; |
|
255 #ifdef G_OS_WIN32 |
|
256 WSADATA w; |
|
257 int error; |
|
258 #endif |
149 |
259 |
150 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); |
260 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); |
|
261 |
|
262 #ifdef G_OS_WIN32 |
|
263 error = WSAStartup (0x0202, &w); |
|
264 |
|
265 if (error) |
|
266 goto startup_error; |
|
267 |
|
268 if (w.wVersion != 0x0202) |
|
269 goto version_error; |
|
270 #endif |
151 |
271 |
152 newconn = g_new0 (GstRTSPConnection, 1); |
272 newconn = g_new0 (GstRTSPConnection, 1); |
153 |
273 |
154 if ((newconn->fdset = gst_poll_new (TRUE)) == NULL) |
274 if ((newconn->fdset = gst_poll_new (TRUE)) == NULL) |
155 goto no_fdset; |
275 goto no_fdset; |
156 |
276 |
157 newconn->url = url; |
277 newconn->url = gst_rtsp_url_copy (url); |
158 newconn->fd.fd = -1; |
278 newconn->fd0.fd = -1; |
|
279 newconn->fd1.fd = -1; |
159 newconn->timer = g_timer_new (); |
280 newconn->timer = g_timer_new (); |
|
281 newconn->timeout = 60; |
|
282 newconn->cseq = 1; |
160 |
283 |
161 newconn->auth_method = GST_RTSP_AUTH_NONE; |
284 newconn->auth_method = GST_RTSP_AUTH_NONE; |
162 newconn->username = NULL; |
285 newconn->username = NULL; |
163 newconn->passwd = NULL; |
286 newconn->passwd = NULL; |
|
287 newconn->auth_params = NULL; |
164 |
288 |
165 *conn = newconn; |
289 *conn = newconn; |
166 |
290 |
167 return GST_RTSP_OK; |
291 return GST_RTSP_OK; |
168 |
292 |
169 /* ERRORS */ |
293 /* ERRORS */ |
|
294 #ifdef G_OS_WIN32 |
|
295 startup_error: |
|
296 { |
|
297 g_warning ("Error %d on WSAStartup", error); |
|
298 return GST_RTSP_EWSASTART; |
|
299 } |
|
300 version_error: |
|
301 { |
|
302 g_warning ("Windows sockets are not version 0x202 (current 0x%x)", |
|
303 w.wVersion); |
|
304 WSACleanup (); |
|
305 return GST_RTSP_EWSAVERSION; |
|
306 } |
|
307 #endif |
170 no_fdset: |
308 no_fdset: |
171 { |
309 { |
172 g_free (newconn); |
310 g_free (newconn); |
|
311 #ifdef G_OS_WIN32 |
|
312 WSACleanup (); |
|
313 #endif |
173 return GST_RTSP_ESYS; |
314 return GST_RTSP_ESYS; |
174 } |
315 } |
175 } |
316 } |
176 |
317 |
177 /** |
318 /** |
178 * gst_rtsp_connection_connect: |
319 * gst_rtsp_connection_accept: |
179 * @conn: a #GstRTSPConnection |
320 * @sock: a socket |
180 * @timeout: a #GTimeVal timeout |
321 * @conn: storage for a #GstRTSPConnection |
181 * |
322 * |
182 * Attempt to connect to the url of @conn made with |
323 * Accept a new connection on @sock and create a new #GstRTSPConnection for |
183 * gst_rtsp_connection_create(). If @timeout is #NULL this function can block |
324 * handling communication on new socket. |
184 * forever. If @timeout contains a valid timeout, this function will return |
325 * |
185 * #GST_RTSP_ETIMEOUT after the timeout expired. |
326 * Returns: #GST_RTSP_OK when @conn contains a valid connection. |
186 * |
327 * |
187 * This function can be cancelled with gst_rtsp_connection_flush(). |
328 * Since: 0.10.23 |
188 * |
|
189 * Returns: #GST_RTSP_OK when a connection could be made. |
|
190 */ |
329 */ |
191 GstRTSPResult |
330 GstRTSPResult |
192 gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout) |
331 gst_rtsp_connection_accept (gint sock, GstRTSPConnection ** conn) |
|
332 { |
|
333 int fd; |
|
334 GstRTSPConnection *newconn = NULL; |
|
335 union gst_sockaddr sa; |
|
336 socklen_t slen = sizeof (sa); |
|
337 gchar ip[INET6_ADDRSTRLEN]; |
|
338 GstRTSPUrl *url; |
|
339 #ifdef G_OS_WIN32 |
|
340 gulong flags = 1; |
|
341 #endif |
|
342 |
|
343 memset (&sa, 0, slen); |
|
344 |
|
345 #ifndef G_OS_WIN32 |
|
346 fd = accept (sock, &sa.sa, &slen); |
|
347 #else |
|
348 fd = accept (sock, &sa.sa, (gint *) & slen); |
|
349 #endif /* G_OS_WIN32 */ |
|
350 if (fd == -1) |
|
351 goto accept_failed; |
|
352 |
|
353 if (getnameinfo (&sa.sa, slen, ip, sizeof (ip), NULL, 0, NI_NUMERICHOST) != 0) |
|
354 goto getnameinfo_failed; |
|
355 if (sa.sa.sa_family != AF_INET && sa.sa.sa_family != AF_INET6) |
|
356 goto wrong_family; |
|
357 |
|
358 /* set to non-blocking mode so that we can cancel the communication */ |
|
359 #ifndef G_OS_WIN32 |
|
360 fcntl (fd, F_SETFL, O_NONBLOCK); |
|
361 #else |
|
362 ioctlsocket (fd, FIONBIO, &flags); |
|
363 #endif /* G_OS_WIN32 */ |
|
364 |
|
365 /* create a url for the client address */ |
|
366 url = g_new0 (GstRTSPUrl, 1); |
|
367 url->host = g_strdup (ip); |
|
368 if (sa.sa.sa_family == AF_INET) |
|
369 url->port = sa.sa_in.sin_port; |
|
370 else |
|
371 url->port = sa.sa_in6.sin6_port; |
|
372 |
|
373 /* now create the connection object */ |
|
374 gst_rtsp_connection_create (url, &newconn); |
|
375 gst_rtsp_url_free (url); |
|
376 |
|
377 ADD_POLLFD (newconn->fdset, &newconn->fd0, fd); |
|
378 |
|
379 /* both read and write initially */ |
|
380 newconn->readfd = &newconn->fd0; |
|
381 newconn->writefd = &newconn->fd0; |
|
382 |
|
383 *conn = newconn; |
|
384 |
|
385 return GST_RTSP_OK; |
|
386 |
|
387 /* ERRORS */ |
|
388 accept_failed: |
|
389 { |
|
390 return GST_RTSP_ESYS; |
|
391 } |
|
392 getnameinfo_failed: |
|
393 wrong_family: |
|
394 { |
|
395 close (fd); |
|
396 return GST_RTSP_ERROR; |
|
397 } |
|
398 } |
|
399 |
|
400 static gchar * |
|
401 do_resolve (const gchar * host) |
|
402 { |
|
403 static gchar ip[INET6_ADDRSTRLEN]; |
|
404 struct addrinfo *aires; |
|
405 struct addrinfo *ai; |
|
406 gint aierr; |
|
407 |
|
408 aierr = getaddrinfo (host, NULL, NULL, &aires); |
|
409 if (aierr != 0) |
|
410 goto no_addrinfo; |
|
411 |
|
412 for (ai = aires; ai; ai = ai->ai_next) { |
|
413 if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) { |
|
414 break; |
|
415 } |
|
416 } |
|
417 if (ai == NULL) |
|
418 goto no_family; |
|
419 |
|
420 aierr = getnameinfo (ai->ai_addr, ai->ai_addrlen, ip, sizeof (ip), NULL, 0, |
|
421 NI_NUMERICHOST | NI_NUMERICSERV); |
|
422 if (aierr != 0) |
|
423 goto no_address; |
|
424 |
|
425 freeaddrinfo (aires); |
|
426 |
|
427 return g_strdup (ip); |
|
428 |
|
429 /* ERRORS */ |
|
430 no_addrinfo: |
|
431 { |
|
432 GST_ERROR ("no addrinfo found for %s: %s", host, gai_strerror (aierr)); |
|
433 return NULL; |
|
434 } |
|
435 no_family: |
|
436 { |
|
437 GST_ERROR ("no family found for %s", host); |
|
438 freeaddrinfo (aires); |
|
439 return NULL; |
|
440 } |
|
441 no_address: |
|
442 { |
|
443 GST_ERROR ("no address found for %s: %s", host, gai_strerror (aierr)); |
|
444 freeaddrinfo (aires); |
|
445 return NULL; |
|
446 } |
|
447 } |
|
448 |
|
449 static GstRTSPResult |
|
450 do_connect (const gchar * ip, guint16 port, GstPollFD * fdout, |
|
451 GstPoll * fdset, GTimeVal * timeout) |
193 { |
452 { |
194 gint fd; |
453 gint fd; |
195 struct sockaddr_in sa_in; |
454 struct addrinfo hints; |
196 struct hostent *hostinfo; |
455 struct addrinfo *aires; |
197 const gchar *ip; |
456 struct addrinfo *ai; |
198 struct in_addr addr; |
457 gint aierr; |
|
458 gchar service[NI_MAXSERV]; |
199 gint ret; |
459 gint ret; |
200 guint16 port; |
460 #ifdef G_OS_WIN32 |
201 GstRTSPUrl *url; |
461 unsigned long flags = 1; |
|
462 #endif /* G_OS_WIN32 */ |
202 GstClockTime to; |
463 GstClockTime to; |
203 gint retval; |
464 gint retval; |
204 |
465 |
205 #ifdef G_OS_WIN32 |
466 memset (&hints, 0, sizeof hints); |
206 unsigned long flags = 1; |
467 hints.ai_flags = AI_NUMERICHOST; |
207 struct in_addr *addrp; |
468 hints.ai_family = AF_UNSPEC; |
208 #else |
469 hints.ai_socktype = SOCK_STREAM; |
209 char **addrs; |
470 g_snprintf (service, sizeof (service) - 1, "%hu", port); |
210 gchar ipbuf[INET_ADDRSTRLEN]; |
471 service[sizeof (service) - 1] = '\0'; |
211 #endif /* G_OS_WIN32 */ |
472 |
212 |
473 aierr = getaddrinfo (ip, service, &hints, &aires); |
213 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); |
474 if (aierr != 0) |
214 g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL); |
475 goto no_addrinfo; |
215 g_return_val_if_fail (conn->fd.fd < 0, GST_RTSP_EINVAL); |
476 |
216 |
477 for (ai = aires; ai; ai = ai->ai_next) { |
217 url = conn->url; |
478 if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) { |
218 |
479 break; |
219 /* first check if it already is an IP address */ |
480 } |
220 if (inet_aton (url->host, &addr)) { |
481 } |
221 ip = url->host; |
482 if (ai == NULL) |
222 } else { |
483 goto no_family; |
223 hostinfo = gethostbyname (url->host); |
484 |
224 if (!hostinfo) |
485 fd = socket (ai->ai_family, SOCK_STREAM, 0); |
225 goto not_resolved; /* h_errno set */ |
|
226 |
|
227 if (hostinfo->h_addrtype != AF_INET) |
|
228 goto not_ip; /* host not an IP host */ |
|
229 #ifdef G_OS_WIN32 |
|
230 addrp = (struct in_addr *) hostinfo->h_addr_list[0]; |
|
231 /* this is not threadsafe */ |
|
232 ip = inet_ntoa (*addrp); |
|
233 #else |
|
234 addrs = hostinfo->h_addr_list; |
|
235 ip = inet_ntop (AF_INET, (struct in_addr *) addrs[0], ipbuf, |
|
236 sizeof (ipbuf)); |
|
237 #endif /* G_OS_WIN32 */ |
|
238 } |
|
239 |
|
240 /* get the port from the url */ |
|
241 gst_rtsp_url_get_port (url, &port); |
|
242 |
|
243 memset (&sa_in, 0, sizeof (sa_in)); |
|
244 sa_in.sin_family = AF_INET; /* network socket */ |
|
245 sa_in.sin_port = htons (port); /* on port */ |
|
246 sa_in.sin_addr.s_addr = inet_addr (ip); /* on host ip */ |
|
247 |
|
248 fd = socket (AF_INET, SOCK_STREAM, 0); |
|
249 if (fd == -1) |
486 if (fd == -1) |
250 goto sys_error; |
487 goto no_socket; |
251 |
488 |
252 /* set to non-blocking mode so that we can cancel the connect */ |
489 /* set to non-blocking mode so that we can cancel the connect */ |
253 #ifndef G_OS_WIN32 |
490 #ifndef G_OS_WIN32 |
254 fcntl (fd, F_SETFL, O_NONBLOCK); |
491 fcntl (fd, F_SETFL, O_NONBLOCK); |
255 #else |
492 #else |
256 ioctlsocket (fd, FIONBIO, &flags); |
493 ioctlsocket (fd, FIONBIO, &flags); |
257 #endif /* G_OS_WIN32 */ |
494 #endif /* G_OS_WIN32 */ |
258 |
495 |
259 /* add the socket to our fdset */ |
496 /* add the socket to our fdset */ |
260 conn->fd.fd = fd; |
497 ADD_POLLFD (fdset, fdout, fd); |
261 gst_poll_add_fd (conn->fdset, &conn->fd); |
|
262 |
498 |
263 /* we are going to connect ASYNC now */ |
499 /* we are going to connect ASYNC now */ |
264 ret = connect (fd, (struct sockaddr *) &sa_in, sizeof (sa_in)); |
500 ret = connect (fd, ai->ai_addr, ai->ai_addrlen); |
265 if (ret == 0) |
501 if (ret == 0) |
266 goto done; |
502 goto done; |
267 if (ERRNO_IS_NOT_EINPROGRESS) |
503 if (!ERRNO_IS_EINPROGRESS) |
268 goto sys_error; |
504 goto sys_error; |
269 |
505 |
270 /* wait for connect to complete up to the specified timeout or until we got |
506 /* wait for connect to complete up to the specified timeout or until we got |
271 * interrupted. */ |
507 * interrupted. */ |
272 gst_poll_fd_ctl_write (conn->fdset, &conn->fd, TRUE); |
508 gst_poll_fd_ctl_write (fdset, fdout, TRUE); |
273 |
509 |
274 to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE; |
510 to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE; |
275 |
511 |
276 do { |
512 do { |
277 retval = gst_poll_wait (conn->fdset, to); |
513 retval = gst_poll_wait (fdset, to); |
278 } while (retval == -1 && (errno == EINTR || errno == EAGAIN)); |
514 } while (retval == -1 && (errno == EINTR || errno == EAGAIN)); |
279 |
515 |
280 if (retval == 0) |
516 if (retval == 0) |
281 goto timeout; |
517 goto timeout; |
282 else if (retval == -1) |
518 else if (retval == -1) |
283 goto sys_error; |
519 goto sys_error; |
284 |
520 |
285 gst_poll_fd_ignored (conn->fdset, &conn->fd); |
521 /* we can still have an error connecting on windows */ |
|
522 if (gst_poll_fd_has_error (fdset, fdout)) { |
|
523 socklen_t len = sizeof (errno); |
|
524 #ifndef G_OS_WIN32 |
|
525 getsockopt (fd, SOL_SOCKET, SO_ERROR, &errno, &len); |
|
526 #else |
|
527 getsockopt (fd, SOL_SOCKET, SO_ERROR, (char *) &errno, &len); |
|
528 #endif |
|
529 goto sys_error; |
|
530 } |
|
531 |
|
532 gst_poll_fd_ignored (fdset, fdout); |
286 |
533 |
287 done: |
534 done: |
288 conn->ip = g_strdup (ip); |
535 freeaddrinfo (aires); |
289 |
536 |
290 return GST_RTSP_OK; |
537 return GST_RTSP_OK; |
291 |
538 |
|
539 /* ERRORS */ |
|
540 no_addrinfo: |
|
541 { |
|
542 GST_ERROR ("no addrinfo found for %s: %s", ip, gai_strerror (aierr)); |
|
543 return GST_RTSP_ERROR; |
|
544 } |
|
545 no_family: |
|
546 { |
|
547 GST_ERROR ("no family found for %s", ip); |
|
548 freeaddrinfo (aires); |
|
549 return GST_RTSP_ERROR; |
|
550 } |
|
551 no_socket: |
|
552 { |
|
553 GST_ERROR ("no socket %d (%s)", errno, g_strerror (errno)); |
|
554 freeaddrinfo (aires); |
|
555 return GST_RTSP_ESYS; |
|
556 } |
292 sys_error: |
557 sys_error: |
293 { |
558 { |
294 if (conn->fd.fd >= 0) { |
559 GST_ERROR ("system error %d (%s)", errno, g_strerror (errno)); |
295 gst_poll_remove_fd (conn->fdset, &conn->fd); |
560 REMOVE_POLLFD (fdset, fdout); |
296 conn->fd.fd = -1; |
561 freeaddrinfo (aires); |
|
562 return GST_RTSP_ESYS; |
|
563 } |
|
564 timeout: |
|
565 { |
|
566 GST_ERROR ("timeout"); |
|
567 REMOVE_POLLFD (fdset, fdout); |
|
568 freeaddrinfo (aires); |
|
569 return GST_RTSP_ETIMEOUT; |
|
570 } |
|
571 } |
|
572 |
|
573 static GstRTSPResult |
|
574 setup_tunneling (GstRTSPConnection * conn, GTimeVal * timeout) |
|
575 { |
|
576 gint i; |
|
577 GstRTSPResult res; |
|
578 gchar *str; |
|
579 guint idx, line; |
|
580 gint retval; |
|
581 GstClockTime to; |
|
582 gchar *ip, *url_port_str; |
|
583 guint16 port, url_port; |
|
584 gchar codestr[4], *resultstr; |
|
585 gint code; |
|
586 GstRTSPUrl *url; |
|
587 gchar *hostparam; |
|
588 |
|
589 /* create a random sessionid */ |
|
590 for (i = 0; i < TUNNELID_LEN; i++) |
|
591 conn->tunnelid[i] = g_random_int_range ('a', 'z'); |
|
592 conn->tunnelid[TUNNELID_LEN - 1] = '\0'; |
|
593 |
|
594 url = conn->url; |
|
595 /* get the port from the url */ |
|
596 gst_rtsp_url_get_port (url, &url_port); |
|
597 |
|
598 if (conn->proxy_host) { |
|
599 hostparam = g_strdup_printf ("Host: %s:%d\r\n", url->host, url_port); |
|
600 url_port_str = g_strdup_printf (":%d", url_port); |
|
601 ip = conn->proxy_host; |
|
602 port = conn->proxy_port; |
|
603 } else { |
|
604 hostparam = NULL; |
|
605 url_port_str = NULL; |
|
606 ip = conn->ip; |
|
607 port = url_port; |
|
608 } |
|
609 |
|
610 /* */ |
|
611 str = g_strdup_printf ("GET %s%s%s%s%s%s HTTP/1.0\r\n" |
|
612 "%s" |
|
613 "x-sessioncookie: %s\r\n" |
|
614 "Accept: application/x-rtsp-tunnelled\r\n" |
|
615 "Pragma: no-cache\r\n" |
|
616 "Cache-Control: no-cache\r\n" "\r\n", |
|
617 conn->proxy_host ? "http://" : "", |
|
618 conn->proxy_host ? url->host : "", |
|
619 conn->proxy_host ? url_port_str : "", |
|
620 url->abspath, url->query ? "?" : "", url->query ? url->query : "", |
|
621 hostparam ? hostparam : "", conn->tunnelid); |
|
622 |
|
623 /* we start by writing to this fd */ |
|
624 conn->writefd = &conn->fd0; |
|
625 |
|
626 res = gst_rtsp_connection_write (conn, (guint8 *) str, strlen (str), timeout); |
|
627 g_free (str); |
|
628 if (res != GST_RTSP_OK) |
|
629 goto write_failed; |
|
630 |
|
631 gst_poll_fd_ctl_write (conn->fdset, &conn->fd0, FALSE); |
|
632 gst_poll_fd_ctl_read (conn->fdset, &conn->fd0, TRUE); |
|
633 |
|
634 to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE; |
|
635 |
|
636 line = 0; |
|
637 while (TRUE) { |
|
638 guint8 buffer[4096]; |
|
639 |
|
640 idx = 0; |
|
641 while (TRUE) { |
|
642 res = read_line (conn->fd0.fd, buffer, &idx, sizeof (buffer), NULL); |
|
643 if (res == GST_RTSP_EEOF) |
|
644 goto eof; |
|
645 if (res == GST_RTSP_OK) |
|
646 break; |
|
647 if (res != GST_RTSP_EINTR) |
|
648 goto read_error; |
|
649 |
|
650 do { |
|
651 retval = gst_poll_wait (conn->fdset, to); |
|
652 } while (retval == -1 && (errno == EINTR || errno == EAGAIN)); |
|
653 |
|
654 /* check for timeout */ |
|
655 if (retval == 0) |
|
656 goto timeout; |
|
657 |
|
658 if (retval == -1) { |
|
659 if (errno == EBUSY) |
|
660 goto stopped; |
|
661 else |
|
662 goto select_error; |
|
663 } |
297 } |
664 } |
298 if (fd >= 0) |
665 |
299 CLOSE_SOCKET (fd); |
666 /* check for last line */ |
300 return GST_RTSP_ESYS; |
667 if (buffer[0] == '\r') |
|
668 buffer[0] = '\0'; |
|
669 if (buffer[0] == '\0') |
|
670 break; |
|
671 |
|
672 if (line == 0) { |
|
673 /* first line, parse response */ |
|
674 gchar versionstr[20]; |
|
675 gchar *bptr; |
|
676 |
|
677 bptr = (gchar *) buffer; |
|
678 |
|
679 parse_string (versionstr, sizeof (versionstr), &bptr); |
|
680 parse_string (codestr, sizeof (codestr), &bptr); |
|
681 code = atoi (codestr); |
|
682 |
|
683 while (g_ascii_isspace (*bptr)) |
|
684 bptr++; |
|
685 |
|
686 resultstr = bptr; |
|
687 |
|
688 if (code != GST_RTSP_STS_OK) |
|
689 goto wrong_result; |
|
690 } else { |
|
691 gchar key[32]; |
|
692 gchar *value; |
|
693 |
|
694 /* other lines, parse key/value */ |
|
695 res = parse_key_value (buffer, key, sizeof (key), &value); |
|
696 if (res == GST_RTSP_OK) { |
|
697 /* we got a new ip address */ |
|
698 if (g_ascii_strcasecmp (key, "x-server-ip-address") == 0) { |
|
699 if (conn->proxy_host) { |
|
700 /* if we use a proxy we need to change the destination url */ |
|
701 g_free (url->host); |
|
702 url->host = g_strdup (value); |
|
703 g_free (hostparam); |
|
704 g_free (url_port_str); |
|
705 hostparam = |
|
706 g_strdup_printf ("Host: %s:%d\r\n", url->host, url_port); |
|
707 url_port_str = g_strdup_printf (":%d", url_port); |
|
708 } else { |
|
709 /* and resolve the new ip address */ |
|
710 if (!(ip = do_resolve (conn->ip))) |
|
711 goto not_resolved; |
|
712 g_free (conn->ip); |
|
713 conn->ip = ip; |
|
714 } |
|
715 } |
|
716 } |
|
717 } |
|
718 line++; |
|
719 } |
|
720 |
|
721 /* connect to the host/port */ |
|
722 res = do_connect (ip, port, &conn->fd1, conn->fdset, timeout); |
|
723 if (res != GST_RTSP_OK) |
|
724 goto connect_failed; |
|
725 |
|
726 /* this is now our writing socket */ |
|
727 conn->writefd = &conn->fd1; |
|
728 |
|
729 /* */ |
|
730 str = g_strdup_printf ("POST %s%s%s%s%s%s HTTP/1.0\r\n" |
|
731 "%s" |
|
732 "x-sessioncookie: %s\r\n" |
|
733 "Content-Type: application/x-rtsp-tunnelled\r\n" |
|
734 "Pragma: no-cache\r\n" |
|
735 "Cache-Control: no-cache\r\n" |
|
736 "Content-Length: 32767\r\n" |
|
737 "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n" |
|
738 "\r\n", |
|
739 conn->proxy_host ? "http://" : "", |
|
740 conn->proxy_host ? url->host : "", |
|
741 conn->proxy_host ? url_port_str : "", |
|
742 url->abspath, url->query ? "?" : "", url->query ? url->query : "", |
|
743 hostparam ? hostparam : "", conn->tunnelid); |
|
744 |
|
745 res = gst_rtsp_connection_write (conn, (guint8 *) str, strlen (str), timeout); |
|
746 g_free (str); |
|
747 if (res != GST_RTSP_OK) |
|
748 goto write_failed; |
|
749 |
|
750 exit: |
|
751 g_free (hostparam); |
|
752 g_free (url_port_str); |
|
753 |
|
754 return res; |
|
755 |
|
756 /* ERRORS */ |
|
757 write_failed: |
|
758 { |
|
759 GST_ERROR ("write failed (%d)", res); |
|
760 goto exit; |
|
761 } |
|
762 eof: |
|
763 { |
|
764 res = GST_RTSP_EEOF; |
|
765 goto exit; |
|
766 } |
|
767 read_error: |
|
768 { |
|
769 goto exit; |
|
770 } |
|
771 timeout: |
|
772 { |
|
773 res = GST_RTSP_ETIMEOUT; |
|
774 goto exit; |
|
775 } |
|
776 select_error: |
|
777 { |
|
778 res = GST_RTSP_ESYS; |
|
779 goto exit; |
|
780 } |
|
781 stopped: |
|
782 { |
|
783 res = GST_RTSP_EINTR; |
|
784 goto exit; |
|
785 } |
|
786 wrong_result: |
|
787 { |
|
788 GST_ERROR ("got failure response %d %s", code, resultstr); |
|
789 res = GST_RTSP_ERROR; |
|
790 goto exit; |
301 } |
791 } |
302 not_resolved: |
792 not_resolved: |
303 { |
793 { |
|
794 GST_ERROR ("could not resolve %s", conn->ip); |
|
795 res = GST_RTSP_ENET; |
|
796 goto exit; |
|
797 } |
|
798 connect_failed: |
|
799 { |
|
800 GST_ERROR ("failed to connect"); |
|
801 goto exit; |
|
802 } |
|
803 } |
|
804 |
|
805 /** |
|
806 * gst_rtsp_connection_connect: |
|
807 * @conn: a #GstRTSPConnection |
|
808 * @timeout: a #GTimeVal timeout |
|
809 * |
|
810 * Attempt to connect to the url of @conn made with |
|
811 * gst_rtsp_connection_create(). If @timeout is #NULL this function can block |
|
812 * forever. If @timeout contains a valid timeout, this function will return |
|
813 * #GST_RTSP_ETIMEOUT after the timeout expired. |
|
814 * |
|
815 * This function can be cancelled with gst_rtsp_connection_flush(). |
|
816 * |
|
817 * Returns: #GST_RTSP_OK when a connection could be made. |
|
818 */ |
|
819 GstRTSPResult |
|
820 gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout) |
|
821 { |
|
822 GstRTSPResult res; |
|
823 gchar *ip; |
|
824 guint16 port; |
|
825 GstRTSPUrl *url; |
|
826 |
|
827 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); |
|
828 g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL); |
|
829 g_return_val_if_fail (conn->fd0.fd < 0, GST_RTSP_EINVAL); |
|
830 |
|
831 url = conn->url; |
|
832 |
|
833 if (conn->proxy_host && conn->tunneled) { |
|
834 if (!(ip = do_resolve (conn->proxy_host))) { |
|
835 GST_ERROR ("could not resolve %s", conn->proxy_host); |
|
836 goto not_resolved; |
|
837 } |
|
838 port = conn->proxy_port; |
|
839 g_free (conn->proxy_host); |
|
840 conn->proxy_host = ip; |
|
841 } else { |
|
842 if (!(ip = do_resolve (url->host))) { |
|
843 GST_ERROR ("could not resolve %s", url->host); |
|
844 goto not_resolved; |
|
845 } |
|
846 /* get the port from the url */ |
|
847 gst_rtsp_url_get_port (url, &port); |
|
848 |
|
849 g_free (conn->ip); |
|
850 conn->ip = ip; |
|
851 } |
|
852 |
|
853 /* connect to the host/port */ |
|
854 res = do_connect (ip, port, &conn->fd0, conn->fdset, timeout); |
|
855 if (res != GST_RTSP_OK) |
|
856 goto connect_failed; |
|
857 |
|
858 /* this is our read URL */ |
|
859 conn->readfd = &conn->fd0; |
|
860 |
|
861 if (conn->tunneled) { |
|
862 res = setup_tunneling (conn, timeout); |
|
863 if (res != GST_RTSP_OK) |
|
864 goto tunneling_failed; |
|
865 } else { |
|
866 conn->writefd = &conn->fd0; |
|
867 } |
|
868 |
|
869 return GST_RTSP_OK; |
|
870 |
|
871 not_resolved: |
|
872 { |
304 return GST_RTSP_ENET; |
873 return GST_RTSP_ENET; |
305 } |
874 } |
306 not_ip: |
875 connect_failed: |
307 { |
876 { |
308 return GST_RTSP_ENOTIP; |
877 GST_ERROR ("failed to connect"); |
309 } |
878 return res; |
310 timeout: |
879 } |
311 { |
880 tunneling_failed: |
312 if (conn->fd.fd >= 0) { |
881 { |
313 gst_poll_remove_fd (conn->fdset, &conn->fd); |
882 GST_ERROR ("failed to setup tunneling"); |
314 conn->fd.fd = -1; |
883 return res; |
315 } |
884 } |
316 if (fd >= 0) |
885 } |
317 CLOSE_SOCKET (fd); |
886 |
318 return GST_RTSP_ETIMEOUT; |
887 static void |
319 } |
888 md5_digest_to_hex_string (unsigned char digest[16], char string[33]) |
|
889 { |
|
890 static const char hexdigits[] = "0123456789abcdef"; |
|
891 int i; |
|
892 |
|
893 for (i = 0; i < 16; i++) { |
|
894 string[i * 2] = hexdigits[(digest[i] >> 4) & 0x0f]; |
|
895 string[i * 2 + 1] = hexdigits[digest[i] & 0x0f]; |
|
896 } |
|
897 string[32] = 0; |
|
898 } |
|
899 |
|
900 static void |
|
901 auth_digest_compute_hex_urp (const gchar * username, |
|
902 const gchar * realm, const gchar * password, gchar hex_urp[33]) |
|
903 { |
|
904 struct MD5Context md5_context; |
|
905 unsigned char digest[16]; |
|
906 |
|
907 MD5Init (&md5_context); |
|
908 MD5Update (&md5_context, username, strlen (username)); |
|
909 MD5Update (&md5_context, ":", 1); |
|
910 MD5Update (&md5_context, realm, strlen (realm)); |
|
911 MD5Update (&md5_context, ":", 1); |
|
912 MD5Update (&md5_context, password, strlen (password)); |
|
913 MD5Final (digest, &md5_context); |
|
914 md5_digest_to_hex_string (digest, hex_urp); |
|
915 } |
|
916 |
|
917 static void |
|
918 auth_digest_compute_response (const gchar * method, |
|
919 const gchar * uri, const gchar * hex_a1, const gchar * nonce, |
|
920 gchar response[33]) |
|
921 { |
|
922 char hex_a2[33]; |
|
923 struct MD5Context md5_context; |
|
924 unsigned char digest[16]; |
|
925 |
|
926 /* compute A2 */ |
|
927 MD5Init (&md5_context); |
|
928 MD5Update (&md5_context, method, strlen (method)); |
|
929 MD5Update (&md5_context, ":", 1); |
|
930 MD5Update (&md5_context, uri, strlen (uri)); |
|
931 MD5Final (digest, &md5_context); |
|
932 md5_digest_to_hex_string (digest, hex_a2); |
|
933 |
|
934 /* compute KD */ |
|
935 MD5Init (&md5_context); |
|
936 MD5Update (&md5_context, hex_a1, strlen (hex_a1)); |
|
937 MD5Update (&md5_context, ":", 1); |
|
938 MD5Update (&md5_context, nonce, strlen (nonce)); |
|
939 MD5Update (&md5_context, ":", 1); |
|
940 |
|
941 MD5Update (&md5_context, hex_a2, 32); |
|
942 MD5Final (digest, &md5_context); |
|
943 md5_digest_to_hex_string (digest, response); |
320 } |
944 } |
321 |
945 |
322 static void |
946 static void |
323 add_auth_header (GstRTSPConnection * conn, GstRTSPMessage * message) |
947 add_auth_header (GstRTSPConnection * conn, GstRTSPMessage * message) |
324 { |
948 { |
325 switch (conn->auth_method) { |
949 switch (conn->auth_method) { |
326 case GST_RTSP_AUTH_BASIC:{ |
950 case GST_RTSP_AUTH_BASIC:{ |
327 gchar *user_pass = |
951 gchar *user_pass; |
328 g_strdup_printf ("%s:%s", conn->username, conn->passwd); |
952 gchar *user_pass64; |
329 gchar *user_pass64 = |
953 gchar *auth_string; |
330 gst_rtsp_base64_encode (user_pass, strlen (user_pass)); |
954 |
331 gchar *auth_string = g_strdup_printf ("Basic %s", user_pass64); |
955 user_pass = g_strdup_printf ("%s:%s", conn->username, conn->passwd); |
332 |
956 user_pass64 = g_base64_encode ((guchar *) user_pass, strlen (user_pass)); |
333 gst_rtsp_message_add_header (message, GST_RTSP_HDR_AUTHORIZATION, |
957 auth_string = g_strdup_printf ("Basic %s", user_pass64); |
|
958 |
|
959 gst_rtsp_message_take_header (message, GST_RTSP_HDR_AUTHORIZATION, |
334 auth_string); |
960 auth_string); |
335 |
961 |
336 g_free (user_pass); |
962 g_free (user_pass); |
337 g_free (user_pass64); |
963 g_free (user_pass64); |
338 g_free (auth_string); |
964 break; |
|
965 } |
|
966 case GST_RTSP_AUTH_DIGEST:{ |
|
967 gchar response[33], hex_urp[33]; |
|
968 gchar *auth_string, *auth_string2; |
|
969 gchar *realm; |
|
970 gchar *nonce; |
|
971 gchar *opaque; |
|
972 const gchar *uri; |
|
973 const gchar *method; |
|
974 |
|
975 /* we need to have some params set */ |
|
976 if (conn->auth_params == NULL) |
|
977 break; |
|
978 |
|
979 /* we need the realm and nonce */ |
|
980 realm = (gchar *) g_hash_table_lookup (conn->auth_params, "realm"); |
|
981 nonce = (gchar *) g_hash_table_lookup (conn->auth_params, "nonce"); |
|
982 if (realm == NULL || nonce == NULL) |
|
983 break; |
|
984 |
|
985 auth_digest_compute_hex_urp (conn->username, realm, conn->passwd, |
|
986 hex_urp); |
|
987 |
|
988 method = gst_rtsp_method_as_text (message->type_data.request.method); |
|
989 uri = message->type_data.request.uri; |
|
990 |
|
991 /* Assume no qop, algorithm=md5, stale=false */ |
|
992 /* For algorithm MD5, a1 = urp. */ |
|
993 auth_digest_compute_response (method, uri, hex_urp, nonce, response); |
|
994 auth_string = g_strdup_printf ("Digest username=\"%s\", " |
|
995 "realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"", |
|
996 conn->username, realm, nonce, uri, response); |
|
997 |
|
998 opaque = (gchar *) g_hash_table_lookup (conn->auth_params, "opaque"); |
|
999 if (opaque) { |
|
1000 auth_string2 = g_strdup_printf ("%s, opaque=\"%s\"", auth_string, |
|
1001 opaque); |
|
1002 g_free (auth_string); |
|
1003 auth_string = auth_string2; |
|
1004 } |
|
1005 gst_rtsp_message_take_header (message, GST_RTSP_HDR_AUTHORIZATION, |
|
1006 auth_string); |
339 break; |
1007 break; |
340 } |
1008 } |
341 default: |
1009 default: |
342 /* Nothing to do */ |
1010 /* Nothing to do */ |
343 break; |
1011 break; |
344 } |
1012 } |
345 } |
1013 } |
346 |
1014 |
347 static void |
1015 static void |
348 add_date_header (GstRTSPMessage * message) |
1016 gen_date_string (gchar * date_string, guint len) |
349 { |
1017 { |
350 GTimeVal tv; |
1018 GTimeVal tv; |
351 gchar date_string[100]; |
|
352 time_t t; |
1019 time_t t; |
353 |
|
354 #ifdef HAVE_GMTIME_R |
1020 #ifdef HAVE_GMTIME_R |
355 struct tm tm_; |
1021 struct tm tm_; |
356 #endif |
1022 #endif |
357 |
1023 |
358 g_get_current_time (&tv); |
1024 g_get_current_time (&tv); |
359 t = (time_t) tv.tv_sec; |
1025 t = (time_t) tv.tv_sec; |
360 |
1026 |
361 #ifdef HAVE_GMTIME_R |
1027 #ifdef HAVE_GMTIME_R |
362 strftime (date_string, sizeof (date_string), "%a, %d %b %Y %H:%M:%S GMT", |
1028 strftime (date_string, len, "%a, %d %b %Y %H:%M:%S GMT", gmtime_r (&t, &tm_)); |
363 gmtime_r (&t, &tm_)); |
|
364 #else |
1029 #else |
365 strftime (date_string, sizeof (date_string), "%a, %d %b %Y %H:%M:%S GMT", |
1030 strftime (date_string, len, "%a, %d %b %Y %H:%M:%S GMT", gmtime (&t)); |
366 gmtime (&t)); |
|
367 #endif |
1031 #endif |
368 |
1032 } |
369 gst_rtsp_message_add_header (message, GST_RTSP_HDR_DATE, date_string); |
1033 |
|
1034 static GstRTSPResult |
|
1035 write_bytes (gint fd, const guint8 * buffer, guint * idx, guint size) |
|
1036 { |
|
1037 guint left; |
|
1038 |
|
1039 if (G_UNLIKELY (*idx > size)) |
|
1040 return GST_RTSP_ERROR; |
|
1041 |
|
1042 left = size - *idx; |
|
1043 |
|
1044 while (left) { |
|
1045 gint r; |
|
1046 |
|
1047 r = WRITE_SOCKET (fd, &buffer[*idx], left); |
|
1048 if (G_UNLIKELY (r == 0)) { |
|
1049 return GST_RTSP_EINTR; |
|
1050 } else if (G_UNLIKELY (r < 0)) { |
|
1051 if (ERRNO_IS_EAGAIN) |
|
1052 return GST_RTSP_EINTR; |
|
1053 if (!ERRNO_IS_EINTR) |
|
1054 return GST_RTSP_ESYS; |
|
1055 } else { |
|
1056 left -= r; |
|
1057 *idx += r; |
|
1058 } |
|
1059 } |
|
1060 return GST_RTSP_OK; |
|
1061 } |
|
1062 |
|
1063 static gint |
|
1064 fill_bytes (gint fd, guint8 * buffer, guint size, DecodeCtx * ctx) |
|
1065 { |
|
1066 gint out = 0; |
|
1067 |
|
1068 if (ctx) { |
|
1069 while (size > 0) { |
|
1070 guint8 in[sizeof (ctx->out) * 4 / 3]; |
|
1071 gint r; |
|
1072 |
|
1073 while (size > 0 && ctx->cout < ctx->coutl) { |
|
1074 /* we have some leftover bytes */ |
|
1075 *buffer++ = ctx->out[ctx->cout++]; |
|
1076 size--; |
|
1077 out++; |
|
1078 } |
|
1079 |
|
1080 /* got what we needed? */ |
|
1081 if (size == 0) |
|
1082 break; |
|
1083 |
|
1084 /* try to read more bytes */ |
|
1085 r = READ_SOCKET (fd, in, sizeof (in)); |
|
1086 if (r <= 0) { |
|
1087 if (out == 0) |
|
1088 out = r; |
|
1089 break; |
|
1090 } |
|
1091 |
|
1092 ctx->cout = 0; |
|
1093 ctx->coutl = |
|
1094 g_base64_decode_step ((gchar *) in, r, ctx->out, &ctx->state, |
|
1095 &ctx->save); |
|
1096 } |
|
1097 } else { |
|
1098 out = READ_SOCKET (fd, buffer, size); |
|
1099 } |
|
1100 |
|
1101 return out; |
|
1102 } |
|
1103 |
|
1104 static GstRTSPResult |
|
1105 read_bytes (gint fd, guint8 * buffer, guint * idx, guint size, DecodeCtx * ctx) |
|
1106 { |
|
1107 guint left; |
|
1108 |
|
1109 if (G_UNLIKELY (*idx > size)) |
|
1110 return GST_RTSP_ERROR; |
|
1111 |
|
1112 left = size - *idx; |
|
1113 |
|
1114 while (left) { |
|
1115 gint r; |
|
1116 |
|
1117 r = fill_bytes (fd, &buffer[*idx], left, ctx); |
|
1118 if (G_UNLIKELY (r == 0)) { |
|
1119 return GST_RTSP_EEOF; |
|
1120 } else if (G_UNLIKELY (r < 0)) { |
|
1121 if (ERRNO_IS_EAGAIN) |
|
1122 return GST_RTSP_EINTR; |
|
1123 if (!ERRNO_IS_EINTR) |
|
1124 return GST_RTSP_ESYS; |
|
1125 } else { |
|
1126 left -= r; |
|
1127 *idx += r; |
|
1128 } |
|
1129 } |
|
1130 return GST_RTSP_OK; |
|
1131 } |
|
1132 |
|
1133 static GstRTSPResult |
|
1134 read_line (gint fd, guint8 * buffer, guint * idx, guint size, DecodeCtx * ctx) |
|
1135 { |
|
1136 while (TRUE) { |
|
1137 guint8 c; |
|
1138 gint r; |
|
1139 |
|
1140 r = fill_bytes (fd, &c, 1, ctx); |
|
1141 if (G_UNLIKELY (r == 0)) { |
|
1142 return GST_RTSP_EEOF; |
|
1143 } else if (G_UNLIKELY (r < 0)) { |
|
1144 if (ERRNO_IS_EAGAIN) |
|
1145 return GST_RTSP_EINTR; |
|
1146 if (!ERRNO_IS_EINTR) |
|
1147 return GST_RTSP_ESYS; |
|
1148 } else { |
|
1149 if (c == '\n') /* end on \n */ |
|
1150 break; |
|
1151 if (c == '\r') /* ignore \r */ |
|
1152 continue; |
|
1153 |
|
1154 if (G_LIKELY (*idx < size - 1)) |
|
1155 buffer[(*idx)++] = c; |
|
1156 } |
|
1157 } |
|
1158 buffer[*idx] = '\0'; |
|
1159 |
|
1160 return GST_RTSP_OK; |
370 } |
1161 } |
371 |
1162 |
372 /** |
1163 /** |
373 * gst_rtsp_connection_write: |
1164 * gst_rtsp_connection_write: |
374 * @conn: a #GstRTSPConnection |
1165 * @conn: a #GstRTSPConnection |
693 return GST_RTSP_EPARSE; |
1449 return GST_RTSP_EPARSE; |
694 } |
1450 } |
695 } |
1451 } |
696 |
1452 |
697 static GstRTSPResult |
1453 static GstRTSPResult |
698 parse_request_line (gchar * buffer, GstRTSPMessage * msg) |
1454 parse_request_line (GstRTSPConnection * conn, guint8 * buffer, |
|
1455 GstRTSPMessage * msg) |
699 { |
1456 { |
700 GstRTSPResult res = GST_RTSP_OK; |
1457 GstRTSPResult res = GST_RTSP_OK; |
701 gchar versionstr[20]; |
1458 gchar versionstr[20]; |
702 gchar methodstr[20]; |
1459 gchar methodstr[20]; |
703 gchar urlstr[4096]; |
1460 gchar urlstr[4096]; |
704 gchar *bptr; |
1461 gchar *bptr; |
705 GstRTSPMethod method; |
1462 GstRTSPMethod method; |
706 |
1463 GstRTSPTunnelState tstate = TUNNEL_STATE_NONE; |
707 bptr = buffer; |
1464 |
708 |
1465 bptr = (gchar *) buffer; |
709 read_string (methodstr, sizeof (methodstr), &bptr); |
1466 |
|
1467 parse_string (methodstr, sizeof (methodstr), &bptr); |
710 method = gst_rtsp_find_method (methodstr); |
1468 method = gst_rtsp_find_method (methodstr); |
711 |
1469 if (method == GST_RTSP_INVALID) { |
712 read_string (urlstr, sizeof (urlstr), &bptr); |
1470 /* a tunnel request is allowed when we don't have one yet */ |
713 if (*urlstr == '\0') |
1471 if (conn->tstate != TUNNEL_STATE_NONE) |
|
1472 goto invalid_method; |
|
1473 /* we need GET or POST for a valid tunnel request */ |
|
1474 if (!strcmp (methodstr, "GET")) |
|
1475 tstate = TUNNEL_STATE_GET; |
|
1476 else if (!strcmp (methodstr, "POST")) |
|
1477 tstate = TUNNEL_STATE_POST; |
|
1478 else |
|
1479 goto invalid_method; |
|
1480 } |
|
1481 |
|
1482 parse_string (urlstr, sizeof (urlstr), &bptr); |
|
1483 if (G_UNLIKELY (*urlstr == '\0')) |
|
1484 goto invalid_url; |
|
1485 |
|
1486 parse_string (versionstr, sizeof (versionstr), &bptr); |
|
1487 |
|
1488 if (G_UNLIKELY (*bptr != '\0')) |
|
1489 goto invalid_version; |
|
1490 |
|
1491 if (strcmp (versionstr, "RTSP/1.0") == 0) { |
|
1492 res = gst_rtsp_message_init_request (msg, method, urlstr); |
|
1493 } else if (strncmp (versionstr, "RTSP/", 5) == 0) { |
|
1494 res = gst_rtsp_message_init_request (msg, method, urlstr); |
|
1495 msg->type_data.request.version = GST_RTSP_VERSION_INVALID; |
|
1496 } else if (strcmp (versionstr, "HTTP/1.0") == 0) { |
|
1497 /* tunnel request, we need a tunnel method */ |
|
1498 if (tstate == TUNNEL_STATE_NONE) { |
|
1499 res = GST_RTSP_EPARSE; |
|
1500 } else { |
|
1501 conn->tstate = tstate; |
|
1502 } |
|
1503 } else { |
714 res = GST_RTSP_EPARSE; |
1504 res = GST_RTSP_EPARSE; |
715 |
|
716 read_string (versionstr, sizeof (versionstr), &bptr); |
|
717 |
|
718 if (*bptr != '\0') |
|
719 res = GST_RTSP_EPARSE; |
|
720 |
|
721 if (strcmp (versionstr, "RTSP/1.0") == 0) { |
|
722 if (gst_rtsp_message_init_request (msg, method, urlstr) != GST_RTSP_OK) |
|
723 res = GST_RTSP_EPARSE; |
|
724 } else if (strncmp (versionstr, "RTSP/", 5) == 0) { |
|
725 if (gst_rtsp_message_init_request (msg, method, urlstr) != GST_RTSP_OK) |
|
726 res = GST_RTSP_EPARSE; |
|
727 msg->type_data.request.version = GST_RTSP_VERSION_INVALID; |
|
728 } else { |
|
729 gst_rtsp_message_init_request (msg, method, urlstr); |
|
730 msg->type_data.request.version = GST_RTSP_VERSION_INVALID; |
|
731 res = GST_RTSP_EPARSE; |
|
732 } |
1505 } |
733 |
1506 |
734 return res; |
1507 return res; |
|
1508 |
|
1509 /* ERRORS */ |
|
1510 invalid_method: |
|
1511 { |
|
1512 GST_ERROR ("invalid method %s", methodstr); |
|
1513 return GST_RTSP_EPARSE; |
|
1514 } |
|
1515 invalid_url: |
|
1516 { |
|
1517 GST_ERROR ("invalid url %s", urlstr); |
|
1518 return GST_RTSP_EPARSE; |
|
1519 } |
|
1520 invalid_version: |
|
1521 { |
|
1522 GST_ERROR ("invalid version"); |
|
1523 return GST_RTSP_EPARSE; |
|
1524 } |
|
1525 } |
|
1526 |
|
1527 static GstRTSPResult |
|
1528 parse_key_value (guint8 * buffer, gchar * key, guint keysize, gchar ** value) |
|
1529 { |
|
1530 gchar *bptr; |
|
1531 |
|
1532 bptr = (gchar *) buffer; |
|
1533 |
|
1534 /* read key */ |
|
1535 parse_key (key, keysize, &bptr); |
|
1536 if (G_UNLIKELY (*bptr != ':')) |
|
1537 goto no_column; |
|
1538 |
|
1539 bptr++; |
|
1540 while (g_ascii_isspace (*bptr)) |
|
1541 bptr++; |
|
1542 |
|
1543 *value = bptr; |
|
1544 |
|
1545 return GST_RTSP_OK; |
|
1546 |
|
1547 /* ERRORS */ |
|
1548 no_column: |
|
1549 { |
|
1550 return GST_RTSP_EPARSE; |
|
1551 } |
735 } |
1552 } |
736 |
1553 |
737 /* parsing lines means reading a Key: Value pair */ |
1554 /* parsing lines means reading a Key: Value pair */ |
738 static GstRTSPResult |
1555 static GstRTSPResult |
739 parse_line (gchar * buffer, GstRTSPMessage * msg) |
1556 parse_line (GstRTSPConnection * conn, guint8 * buffer, GstRTSPMessage * msg) |
740 { |
1557 { |
|
1558 GstRTSPResult res; |
741 gchar key[32]; |
1559 gchar key[32]; |
742 gchar *bptr; |
1560 gchar *value; |
743 GstRTSPHeaderField field; |
1561 GstRTSPHeaderField field; |
744 |
1562 |
745 bptr = buffer; |
1563 res = parse_key_value (buffer, key, sizeof (key), &value); |
746 |
1564 if (G_UNLIKELY (res != GST_RTSP_OK)) |
747 /* read key */ |
1565 goto parse_error; |
748 read_key (key, sizeof (key), &bptr); |
1566 |
749 if (*bptr != ':') |
1567 if (conn->tstate == TUNNEL_STATE_GET || conn->tstate == TUNNEL_STATE_POST) { |
750 goto no_column; |
1568 /* save the tunnel session in the connection */ |
751 |
1569 if (!strcmp (key, "x-sessioncookie")) { |
752 bptr++; |
1570 strncpy (conn->tunnelid, value, TUNNELID_LEN); |
753 |
1571 conn->tunnelid[TUNNELID_LEN - 1] = '\0'; |
754 field = gst_rtsp_find_header_field (key); |
1572 conn->tunneled = TRUE; |
755 if (field != GST_RTSP_HDR_INVALID) { |
1573 } |
756 while (g_ascii_isspace (*bptr)) |
1574 } else { |
757 bptr++; |
1575 field = gst_rtsp_find_header_field (key); |
758 gst_rtsp_message_add_header (msg, field, bptr); |
1576 if (field != GST_RTSP_HDR_INVALID) |
|
1577 gst_rtsp_message_add_header (msg, field, value); |
759 } |
1578 } |
760 |
1579 |
761 return GST_RTSP_OK; |
1580 return GST_RTSP_OK; |
762 |
1581 |
763 no_column: |
1582 /* ERRORS */ |
764 { |
1583 parse_error: |
765 return GST_RTSP_EPARSE; |
1584 { |
766 } |
1585 return res; |
767 } |
1586 } |
768 |
1587 } |
769 /** |
1588 |
770 * gst_rtsp_connection_read_internal: |
1589 /* returns: |
|
1590 * GST_RTSP_OK when a complete message was read. |
|
1591 * GST_RTSP_EEOF: when the socket is closed |
|
1592 * GST_RTSP_EINTR: when more data is needed. |
|
1593 * GST_RTSP_..: some other error occured. |
|
1594 */ |
|
1595 static GstRTSPResult |
|
1596 build_next (GstRTSPBuilder * builder, GstRTSPMessage * message, |
|
1597 GstRTSPConnection * conn) |
|
1598 { |
|
1599 GstRTSPResult res; |
|
1600 |
|
1601 while (TRUE) { |
|
1602 switch (builder->state) { |
|
1603 case STATE_START: |
|
1604 builder->offset = 0; |
|
1605 res = |
|
1606 read_bytes (conn->readfd->fd, (guint8 *) builder->buffer, |
|
1607 &builder->offset, 1, conn->ctxp); |
|
1608 if (res != GST_RTSP_OK) |
|
1609 goto done; |
|
1610 |
|
1611 /* we have 1 bytes now and we can see if this is a data message or |
|
1612 * not */ |
|
1613 if (builder->buffer[0] == '$') { |
|
1614 /* data message, prepare for the header */ |
|
1615 builder->state = STATE_DATA_HEADER; |
|
1616 } else { |
|
1617 builder->line = 0; |
|
1618 builder->state = STATE_READ_LINES; |
|
1619 } |
|
1620 break; |
|
1621 case STATE_DATA_HEADER: |
|
1622 { |
|
1623 res = |
|
1624 read_bytes (conn->readfd->fd, (guint8 *) builder->buffer, |
|
1625 &builder->offset, 4, conn->ctxp); |
|
1626 if (res != GST_RTSP_OK) |
|
1627 goto done; |
|
1628 |
|
1629 gst_rtsp_message_init_data (message, builder->buffer[1]); |
|
1630 |
|
1631 builder->body_len = (builder->buffer[2] << 8) | builder->buffer[3]; |
|
1632 builder->body_data = g_malloc (builder->body_len + 1); |
|
1633 builder->body_data[builder->body_len] = '\0'; |
|
1634 builder->offset = 0; |
|
1635 builder->state = STATE_DATA_BODY; |
|
1636 break; |
|
1637 } |
|
1638 case STATE_DATA_BODY: |
|
1639 { |
|
1640 res = |
|
1641 read_bytes (conn->readfd->fd, builder->body_data, &builder->offset, |
|
1642 builder->body_len, conn->ctxp); |
|
1643 if (res != GST_RTSP_OK) |
|
1644 goto done; |
|
1645 |
|
1646 /* we have the complete body now, store in the message adjusting the |
|
1647 * length to include the traling '\0' */ |
|
1648 gst_rtsp_message_take_body (message, |
|
1649 (guint8 *) builder->body_data, builder->body_len + 1); |
|
1650 builder->body_data = NULL; |
|
1651 builder->body_len = 0; |
|
1652 |
|
1653 builder->state = STATE_END; |
|
1654 break; |
|
1655 } |
|
1656 case STATE_READ_LINES: |
|
1657 { |
|
1658 res = read_line (conn->readfd->fd, builder->buffer, &builder->offset, |
|
1659 sizeof (builder->buffer), conn->ctxp); |
|
1660 if (res != GST_RTSP_OK) |
|
1661 goto done; |
|
1662 |
|
1663 /* we have a regular response */ |
|
1664 if (builder->buffer[0] == '\r') { |
|
1665 builder->buffer[0] = '\0'; |
|
1666 } |
|
1667 |
|
1668 if (builder->buffer[0] == '\0') { |
|
1669 gchar *hdrval; |
|
1670 |
|
1671 /* empty line, end of message header */ |
|
1672 /* see if there is a Content-Length header */ |
|
1673 if (gst_rtsp_message_get_header (message, |
|
1674 GST_RTSP_HDR_CONTENT_LENGTH, &hdrval, 0) == GST_RTSP_OK) { |
|
1675 /* there is, prepare to read the body */ |
|
1676 builder->body_len = atol (hdrval); |
|
1677 builder->body_data = g_malloc (builder->body_len + 1); |
|
1678 builder->body_data[builder->body_len] = '\0'; |
|
1679 builder->offset = 0; |
|
1680 builder->state = STATE_DATA_BODY; |
|
1681 } else { |
|
1682 builder->state = STATE_END; |
|
1683 } |
|
1684 break; |
|
1685 } |
|
1686 |
|
1687 /* we have a line */ |
|
1688 if (builder->line == 0) { |
|
1689 /* first line, check for response status */ |
|
1690 if (memcmp (builder->buffer, "RTSP", 4) == 0) { |
|
1691 res = parse_response_status (builder->buffer, message); |
|
1692 } else { |
|
1693 res = parse_request_line (conn, builder->buffer, message); |
|
1694 } |
|
1695 /* the first line must parse without errors */ |
|
1696 if (res != GST_RTSP_OK) |
|
1697 goto done; |
|
1698 } else { |
|
1699 /* else just parse the line, ignore errors */ |
|
1700 parse_line (conn, builder->buffer, message); |
|
1701 } |
|
1702 builder->line++; |
|
1703 builder->offset = 0; |
|
1704 break; |
|
1705 } |
|
1706 case STATE_END: |
|
1707 { |
|
1708 gchar *session_id; |
|
1709 |
|
1710 if (conn->tstate == TUNNEL_STATE_GET) { |
|
1711 res = GST_RTSP_ETGET; |
|
1712 goto done; |
|
1713 } else if (conn->tstate == TUNNEL_STATE_POST) { |
|
1714 res = GST_RTSP_ETPOST; |
|
1715 goto done; |
|
1716 } |
|
1717 |
|
1718 if (message->type == GST_RTSP_MESSAGE_DATA) { |
|
1719 /* data messages don't have headers */ |
|
1720 res = GST_RTSP_OK; |
|
1721 goto done; |
|
1722 } |
|
1723 |
|
1724 /* save session id in the connection for further use */ |
|
1725 if (message->type == GST_RTSP_MESSAGE_RESPONSE && |
|
1726 gst_rtsp_message_get_header (message, GST_RTSP_HDR_SESSION, |
|
1727 &session_id, 0) == GST_RTSP_OK) { |
|
1728 gint maxlen, i; |
|
1729 |
|
1730 maxlen = sizeof (conn->session_id) - 1; |
|
1731 /* the sessionid can have attributes marked with ; |
|
1732 * Make sure we strip them */ |
|
1733 for (i = 0; session_id[i] != '\0'; i++) { |
|
1734 if (session_id[i] == ';') { |
|
1735 maxlen = i; |
|
1736 /* parse timeout */ |
|
1737 do { |
|
1738 i++; |
|
1739 } while (g_ascii_isspace (session_id[i])); |
|
1740 if (g_str_has_prefix (&session_id[i], "timeout=")) { |
|
1741 gint to; |
|
1742 |
|
1743 /* if we parsed something valid, configure */ |
|
1744 if ((to = atoi (&session_id[i + 8])) > 0) |
|
1745 conn->timeout = to; |
|
1746 } |
|
1747 break; |
|
1748 } |
|
1749 } |
|
1750 |
|
1751 /* make sure to not overflow */ |
|
1752 strncpy (conn->session_id, session_id, maxlen); |
|
1753 conn->session_id[maxlen] = '\0'; |
|
1754 } |
|
1755 res = GST_RTSP_OK; |
|
1756 goto done; |
|
1757 } |
|
1758 default: |
|
1759 res = GST_RTSP_ERROR; |
|
1760 break; |
|
1761 } |
|
1762 } |
|
1763 done: |
|
1764 return res; |
|
1765 } |
|
1766 |
|
1767 /** |
|
1768 * gst_rtsp_connection_read: |
771 * @conn: a #GstRTSPConnection |
1769 * @conn: a #GstRTSPConnection |
772 * @data: the data to read |
1770 * @data: the data to read |
773 * @size: the size of @data |
1771 * @size: the size of @data |
774 * @timeout: a timeout value or #NULL |
1772 * @timeout: a timeout value or #NULL |
775 * @allow_interrupt: can the pending read be interrupted |
|
776 * |
1773 * |
777 * Attempt to read @size bytes into @data from the connected @conn, blocking up to |
1774 * Attempt to read @size bytes into @data from the connected @conn, blocking up to |
778 * the specified @timeout. @timeout can be #NULL, in which case this function |
1775 * the specified @timeout. @timeout can be #NULL, in which case this function |
779 * might block forever. |
1776 * might block forever. |
780 * |
1777 * |
781 * This function can be cancelled with gst_rtsp_connection_flush() only if |
1778 * This function can be cancelled with gst_rtsp_connection_flush(). |
782 * @allow_interrupt is set. |
|
783 * |
1779 * |
784 * Returns: #GST_RTSP_OK on success. |
1780 * Returns: #GST_RTSP_OK on success. |
785 */ |
1781 */ |
786 static GstRTSPResult |
1782 GstRTSPResult |
787 gst_rtsp_connection_read_internal (GstRTSPConnection * conn, guint8 * data, |
1783 gst_rtsp_connection_read (GstRTSPConnection * conn, guint8 * data, guint size, |
788 guint size, GTimeVal * timeout, gboolean allow_interrupt) |
1784 GTimeVal * timeout) |
789 { |
1785 { |
790 guint toread; |
1786 guint offset; |
791 gint retval; |
1787 gint retval; |
792 GstClockTime to; |
1788 GstClockTime to; |
793 FIONREAD_TYPE avail; |
1789 GstRTSPResult res; |
794 |
1790 |
795 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); |
1791 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); |
796 g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL); |
1792 g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL); |
797 |
1793 g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL); |
798 if (size == 0) |
1794 |
|
1795 if (G_UNLIKELY (size == 0)) |
799 return GST_RTSP_OK; |
1796 return GST_RTSP_OK; |
800 |
1797 |
801 toread = size; |
1798 offset = 0; |
802 |
1799 |
803 /* configure timeout if any */ |
1800 /* configure timeout if any */ |
804 to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE; |
1801 to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE; |
805 |
1802 |
806 /* if the call fails, just go in the select.. it should not fail. Else if |
1803 gst_poll_set_controllable (conn->fdset, TRUE); |
807 * there is enough data to read, skip the select call al together.*/ |
1804 gst_poll_fd_ctl_write (conn->fdset, conn->writefd, FALSE); |
808 if (IOCTL_SOCKET (conn->fd.fd, FIONREAD, &avail) < 0) |
1805 gst_poll_fd_ctl_read (conn->fdset, conn->readfd, TRUE); |
809 avail = 0; |
1806 |
810 else if (avail >= toread) |
1807 while (TRUE) { |
811 goto do_read; |
1808 res = read_bytes (conn->readfd->fd, data, &offset, size, conn->ctxp); |
812 |
1809 if (G_UNLIKELY (res == GST_RTSP_EEOF)) |
813 gst_poll_set_controllable (conn->fdset, allow_interrupt); |
1810 goto eof; |
814 gst_poll_fd_ctl_write (conn->fdset, &conn->fd, FALSE); |
1811 if (G_LIKELY (res == GST_RTSP_OK)) |
815 gst_poll_fd_ctl_read (conn->fdset, &conn->fd, TRUE); |
1812 break; |
816 |
1813 if (G_UNLIKELY (res != GST_RTSP_EINTR)) |
817 while (toread > 0) { |
1814 goto read_error; |
818 gint bytes; |
|
819 |
1815 |
820 do { |
1816 do { |
821 retval = gst_poll_wait (conn->fdset, to); |
1817 retval = gst_poll_wait (conn->fdset, to); |
822 } while (retval == -1 && (errno == EINTR || errno == EAGAIN)); |
1818 } while (retval == -1 && (errno == EINTR || errno == EAGAIN)); |
823 |
1819 |
824 if (retval == -1) { |
1820 /* check for timeout */ |
|
1821 if (G_UNLIKELY (retval == 0)) |
|
1822 goto select_timeout; |
|
1823 |
|
1824 if (G_UNLIKELY (retval == -1)) { |
825 if (errno == EBUSY) |
1825 if (errno == EBUSY) |
826 goto stopped; |
1826 goto stopped; |
827 else |
1827 else |
828 goto select_error; |
1828 goto select_error; |
829 } |
1829 } |
830 |
1830 gst_poll_set_controllable (conn->fdset, FALSE); |
831 /* check for timeout */ |
|
832 if (retval == 0) |
|
833 goto select_timeout; |
|
834 |
|
835 do_read: |
|
836 /* if we get here there is activity on the real fd since the select |
|
837 * completed and the control socket was not readable. */ |
|
838 bytes = READ_SOCKET (conn->fd.fd, data, toread); |
|
839 if (bytes == 0) { |
|
840 goto eof; |
|
841 } else if (bytes < 0) { |
|
842 if (ERRNO_IS_NOT_EAGAIN && ERRNO_IS_NOT_EINTR) |
|
843 goto read_error; |
|
844 } else { |
|
845 toread -= bytes; |
|
846 data += bytes; |
|
847 } |
|
848 } |
1831 } |
849 return GST_RTSP_OK; |
1832 return GST_RTSP_OK; |
850 |
1833 |
851 /* ERRORS */ |
1834 /* ERRORS */ |
852 select_error: |
1835 select_error: |
944 */ |
1904 */ |
945 GstRTSPResult |
1905 GstRTSPResult |
946 gst_rtsp_connection_receive (GstRTSPConnection * conn, GstRTSPMessage * message, |
1906 gst_rtsp_connection_receive (GstRTSPConnection * conn, GstRTSPMessage * message, |
947 GTimeVal * timeout) |
1907 GTimeVal * timeout) |
948 { |
1908 { |
949 gchar buffer[4096]; |
|
950 gint line; |
|
951 glong content_length; |
|
952 GstRTSPResult res; |
1909 GstRTSPResult res; |
953 gboolean need_body; |
1910 GstRTSPBuilder builder; |
|
1911 gint retval; |
|
1912 GstClockTime to; |
954 |
1913 |
955 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); |
1914 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); |
956 g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL); |
1915 g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL); |
957 |
1916 g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL); |
958 line = 0; |
1917 |
959 |
1918 /* configure timeout if any */ |
960 need_body = TRUE; |
1919 to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE; |
961 |
1920 |
962 res = GST_RTSP_OK; |
1921 gst_poll_set_controllable (conn->fdset, TRUE); |
963 /* parse first line and headers */ |
1922 gst_poll_fd_ctl_write (conn->fdset, conn->writefd, FALSE); |
964 while (res == GST_RTSP_OK) { |
1923 gst_poll_fd_ctl_read (conn->fdset, conn->readfd, TRUE); |
965 guint8 c; |
1924 |
966 |
1925 memset (&builder, 0, sizeof (GstRTSPBuilder)); |
967 /* read first character, this identifies data messages */ |
1926 while (TRUE) { |
968 /* This is the only read() that we allow to be interrupted */ |
1927 res = build_next (&builder, message, conn); |
969 GST_RTSP_CHECK (gst_rtsp_connection_read_internal (conn, &c, 1, timeout, |
1928 if (G_UNLIKELY (res == GST_RTSP_EEOF)) |
970 TRUE), read_error); |
1929 goto eof; |
971 |
1930 if (G_LIKELY (res == GST_RTSP_OK)) |
972 /* check for data packet, first character is $ */ |
|
973 if (c == '$') { |
|
974 guint16 size; |
|
975 |
|
976 /* data packets are $<1 byte channel><2 bytes length,BE><data bytes> */ |
|
977 |
|
978 /* read channel, which is the next char */ |
|
979 GST_RTSP_CHECK (gst_rtsp_connection_read_internal (conn, &c, 1, timeout, |
|
980 FALSE), read_error); |
|
981 |
|
982 /* now we create a data message */ |
|
983 gst_rtsp_message_init_data (message, c); |
|
984 |
|
985 /* next two bytes are the length of the data */ |
|
986 GST_RTSP_CHECK (gst_rtsp_connection_read_internal (conn, |
|
987 (guint8 *) & size, 2, timeout, FALSE), read_error); |
|
988 |
|
989 size = GUINT16_FROM_BE (size); |
|
990 |
|
991 /* and read the body */ |
|
992 res = read_body (conn, size, message, timeout); |
|
993 need_body = FALSE; |
|
994 break; |
1931 break; |
995 } else { |
1932 if (res == GST_RTSP_ETGET) { |
996 gint offset = 0; |
1933 GString *str; |
997 |
1934 |
998 /* we have a regular response */ |
1935 /* tunnel GET request, we can reply now */ |
999 if (c != '\r') { |
1936 str = gen_tunnel_reply (conn, GST_RTSP_STS_OK); |
1000 buffer[0] = c; |
1937 res = |
1001 offset = 1; |
1938 gst_rtsp_connection_write (conn, (guint8 *) str->str, str->len, |
1002 } |
1939 timeout); |
1003 /* should not happen */ |
1940 g_string_free (str, TRUE); |
1004 if (c == '\n') |
1941 } else if (res == GST_RTSP_ETPOST) { |
1005 break; |
1942 /* tunnel POST request, return the value, the caller now has to link the |
1006 |
1943 * two connections. */ |
1007 /* read lines */ |
1944 break; |
1008 GST_RTSP_CHECK (read_line (conn->fd.fd, buffer + offset, |
1945 } else if (G_UNLIKELY (res != GST_RTSP_EINTR)) |
1009 sizeof (buffer) - offset), read_error); |
1946 goto read_error; |
1010 |
1947 |
1011 if (buffer[0] == '\0') |
1948 do { |
1012 break; |
1949 retval = gst_poll_wait (conn->fdset, to); |
1013 |
1950 } while (retval == -1 && (errno == EINTR || errno == EAGAIN)); |
1014 if (line == 0) { |
1951 |
1015 /* first line, check for response status */ |
1952 /* check for timeout */ |
1016 if (g_str_has_prefix (buffer, "RTSP")) { |
1953 if (G_UNLIKELY (retval == 0)) |
1017 res = parse_response_status (buffer, message); |
1954 goto select_timeout; |
1018 } else { |
1955 |
1019 res = parse_request_line (buffer, message); |
1956 if (G_UNLIKELY (retval == -1)) { |
1020 } |
1957 if (errno == EBUSY) |
1021 } else { |
1958 goto stopped; |
1022 /* else just parse the line */ |
1959 else |
1023 parse_line (buffer, message); |
1960 goto select_error; |
1024 } |
|
1025 } |
1961 } |
1026 line++; |
1962 gst_poll_set_controllable (conn->fdset, FALSE); |
1027 } |
1963 } |
1028 |
1964 |
1029 /* read the rest of the body if needed */ |
1965 /* we have a message here */ |
1030 if (need_body) { |
1966 build_reset (&builder); |
1031 gchar *session_id; |
1967 |
1032 gchar *hdrval; |
1968 return GST_RTSP_OK; |
1033 |
1969 |
1034 /* see if there is a Content-Length header */ |
1970 /* ERRORS */ |
1035 if (gst_rtsp_message_get_header (message, GST_RTSP_HDR_CONTENT_LENGTH, |
1971 select_error: |
1036 &hdrval, 0) == GST_RTSP_OK) { |
1972 { |
1037 /* there is, read the body */ |
1973 res = GST_RTSP_ESYS; |
1038 content_length = atol (hdrval); |
1974 goto cleanup; |
1039 GST_RTSP_CHECK (read_body (conn, content_length, message, timeout), |
1975 } |
1040 read_error); |
1976 select_timeout: |
1041 } |
1977 { |
1042 |
1978 res = GST_RTSP_ETIMEOUT; |
1043 /* save session id in the connection for further use */ |
1979 goto cleanup; |
1044 if (gst_rtsp_message_get_header (message, GST_RTSP_HDR_SESSION, |
1980 } |
1045 &session_id, 0) == GST_RTSP_OK) { |
1981 stopped: |
1046 gint maxlen, i; |
1982 { |
1047 |
1983 res = GST_RTSP_EINTR; |
1048 /* default session timeout */ |
1984 goto cleanup; |
1049 conn->timeout = 60; |
1985 } |
1050 |
1986 eof: |
1051 maxlen = sizeof (conn->session_id) - 1; |
1987 { |
1052 /* the sessionid can have attributes marked with ; |
1988 res = GST_RTSP_EEOF; |
1053 * Make sure we strip them */ |
1989 goto cleanup; |
1054 for (i = 0; session_id[i] != '\0'; i++) { |
1990 } |
1055 if (session_id[i] == ';') { |
|
1056 maxlen = i; |
|
1057 /* parse timeout */ |
|
1058 do { |
|
1059 i++; |
|
1060 } while (g_ascii_isspace (session_id[i])); |
|
1061 if (g_str_has_prefix (&session_id[i], "timeout=")) { |
|
1062 gint to; |
|
1063 |
|
1064 /* if we parsed something valid, configure */ |
|
1065 if ((to = atoi (&session_id[i + 9])) > 0) |
|
1066 conn->timeout = to; |
|
1067 } |
|
1068 break; |
|
1069 } |
|
1070 } |
|
1071 |
|
1072 /* make sure to not overflow */ |
|
1073 strncpy (conn->session_id, session_id, maxlen); |
|
1074 conn->session_id[maxlen] = '\0'; |
|
1075 } |
|
1076 } |
|
1077 return res; |
|
1078 |
|
1079 read_error: |
1991 read_error: |
1080 { |
1992 cleanup: |
|
1993 { |
|
1994 build_reset (&builder); |
|
1995 gst_rtsp_message_unset (message); |
1081 return res; |
1996 return res; |
1082 } |
1997 } |
1083 } |
1998 } |
1084 |
1999 |
1085 /** |
2000 /** |
1086 * gst_rtsp_connection_close: |
2001 * gst_rtsp_connection_close: |
1087 * @conn: a #GstRTSPConnection |
2002 * @conn: a #GstRTSPConnection |
1088 * |
2003 * |
1089 * Close the connected @conn. |
2004 * Close the connected @conn. After this call, the connection is in the same |
|
2005 * state as when it was first created. |
1090 * |
2006 * |
1091 * Returns: #GST_RTSP_OK on success. |
2007 * Returns: #GST_RTSP_OK on success. |
1092 */ |
2008 */ |
1093 GstRTSPResult |
2009 GstRTSPResult |
1094 gst_rtsp_connection_close (GstRTSPConnection * conn) |
2010 gst_rtsp_connection_close (GstRTSPConnection * conn) |
1095 { |
2011 { |
1096 gint res; |
|
1097 |
|
1098 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); |
2012 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); |
1099 |
2013 |
1100 g_free (conn->ip); |
2014 g_free (conn->ip); |
1101 conn->ip = NULL; |
2015 conn->ip = NULL; |
1102 |
2016 |
1103 if (conn->fd.fd != -1) { |
2017 REMOVE_POLLFD (conn->fdset, &conn->fd0); |
1104 gst_poll_remove_fd (conn->fdset, &conn->fd); |
2018 REMOVE_POLLFD (conn->fdset, &conn->fd1); |
1105 res = CLOSE_SOCKET (conn->fd.fd); |
2019 conn->writefd = NULL; |
1106 conn->fd.fd = -1; |
2020 conn->readfd = NULL; |
1107 #ifdef G_OS_WIN32 |
2021 conn->tunneled = FALSE; |
1108 WSACleanup (); |
2022 conn->tstate = TUNNEL_STATE_NONE; |
1109 #endif |
2023 conn->ctxp = NULL; |
1110 if (res != 0) |
2024 g_free (conn->username); |
1111 goto sys_error; |
2025 conn->username = NULL; |
1112 } |
2026 g_free (conn->passwd); |
|
2027 conn->passwd = NULL; |
|
2028 gst_rtsp_connection_clear_auth_params (conn); |
|
2029 conn->timeout = 60; |
|
2030 conn->cseq = 0; |
|
2031 conn->session_id[0] = '\0'; |
1113 |
2032 |
1114 return GST_RTSP_OK; |
2033 return GST_RTSP_OK; |
1115 |
|
1116 sys_error: |
|
1117 { |
|
1118 return GST_RTSP_ESYS; |
|
1119 } |
|
1120 } |
2034 } |
1121 |
2035 |
1122 /** |
2036 /** |
1123 * gst_rtsp_connection_free: |
2037 * gst_rtsp_connection_free: |
1124 * @conn: a #GstRTSPConnection |
2038 * @conn: a #GstRTSPConnection |
1336 conn->username = g_strdup (user); |
2280 conn->username = g_strdup (user); |
1337 conn->passwd = g_strdup (pass); |
2281 conn->passwd = g_strdup (pass); |
1338 |
2282 |
1339 return GST_RTSP_OK; |
2283 return GST_RTSP_OK; |
1340 } |
2284 } |
|
2285 |
|
2286 /** |
|
2287 * str_case_hash: |
|
2288 * @key: ASCII string to hash |
|
2289 * |
|
2290 * Hashes @key in a case-insensitive manner. |
|
2291 * |
|
2292 * Returns: the hash code. |
|
2293 **/ |
|
2294 static guint |
|
2295 str_case_hash (gconstpointer key) |
|
2296 { |
|
2297 const char *p = key; |
|
2298 guint h = g_ascii_toupper (*p); |
|
2299 |
|
2300 if (h) |
|
2301 for (p += 1; *p != '\0'; p++) |
|
2302 h = (h << 5) - h + g_ascii_toupper (*p); |
|
2303 |
|
2304 return h; |
|
2305 } |
|
2306 |
|
2307 /** |
|
2308 * str_case_equal: |
|
2309 * @v1: an ASCII string |
|
2310 * @v2: another ASCII string |
|
2311 * |
|
2312 * Compares @v1 and @v2 in a case-insensitive manner |
|
2313 * |
|
2314 * Returns: %TRUE if they are equal (modulo case) |
|
2315 **/ |
|
2316 static gboolean |
|
2317 str_case_equal (gconstpointer v1, gconstpointer v2) |
|
2318 { |
|
2319 const char *string1 = v1; |
|
2320 const char *string2 = v2; |
|
2321 |
|
2322 return g_ascii_strcasecmp (string1, string2) == 0; |
|
2323 } |
|
2324 |
|
2325 /** |
|
2326 * gst_rtsp_connection_set_auth_param: |
|
2327 * @conn: a #GstRTSPConnection |
|
2328 * @param: authentication directive |
|
2329 * @value: value |
|
2330 * |
|
2331 * Setup @conn with authentication directives. This is not necesary for |
|
2332 * methods #GST_RTSP_AUTH_NONE and #GST_RTSP_AUTH_BASIC. For |
|
2333 * #GST_RTSP_AUTH_DIGEST, directives should be taken from the digest challenge |
|
2334 * in the WWW-Authenticate response header and can include realm, domain, |
|
2335 * nonce, opaque, stale, algorithm, qop as per RFC2617. |
|
2336 * |
|
2337 * Since: 0.10.20 |
|
2338 */ |
|
2339 void |
|
2340 gst_rtsp_connection_set_auth_param (GstRTSPConnection * conn, |
|
2341 const gchar * param, const gchar * value) |
|
2342 { |
|
2343 g_return_if_fail (conn != NULL); |
|
2344 g_return_if_fail (param != NULL); |
|
2345 |
|
2346 if (conn->auth_params == NULL) { |
|
2347 conn->auth_params = |
|
2348 g_hash_table_new_full (str_case_hash, str_case_equal, g_free, g_free); |
|
2349 } |
|
2350 g_hash_table_insert (conn->auth_params, g_strdup (param), g_strdup (value)); |
|
2351 } |
|
2352 |
|
2353 /** |
|
2354 * gst_rtsp_connection_clear_auth_params: |
|
2355 * @conn: a #GstRTSPConnection |
|
2356 * |
|
2357 * Clear the list of authentication directives stored in @conn. |
|
2358 * |
|
2359 * Since: 0.10.20 |
|
2360 */ |
|
2361 void |
|
2362 gst_rtsp_connection_clear_auth_params (GstRTSPConnection * conn) |
|
2363 { |
|
2364 g_return_if_fail (conn != NULL); |
|
2365 |
|
2366 if (conn->auth_params != NULL) { |
|
2367 g_hash_table_destroy (conn->auth_params); |
|
2368 conn->auth_params = NULL; |
|
2369 } |
|
2370 } |
|
2371 |
|
2372 static GstRTSPResult |
|
2373 set_qos_dscp (gint fd, guint qos_dscp) |
|
2374 { |
|
2375 union gst_sockaddr sa; |
|
2376 socklen_t slen = sizeof (sa); |
|
2377 gint af; |
|
2378 gint tos; |
|
2379 |
|
2380 if (fd == -1) |
|
2381 return GST_RTSP_OK; |
|
2382 |
|
2383 if (getsockname (fd, &sa.sa, &slen) < 0) |
|
2384 goto no_getsockname; |
|
2385 |
|
2386 af = sa.sa.sa_family; |
|
2387 |
|
2388 /* if this is an IPv4-mapped address then do IPv4 QoS */ |
|
2389 if (af == AF_INET6) { |
|
2390 if (IN6_IS_ADDR_V4MAPPED (&sa.sa_in6.sin6_addr)) |
|
2391 af = AF_INET; |
|
2392 } |
|
2393 |
|
2394 /* extract and shift 6 bits of the DSCP */ |
|
2395 tos = (qos_dscp & 0x3f) << 2; |
|
2396 |
|
2397 switch (af) { |
|
2398 case AF_INET: |
|
2399 if (SETSOCKOPT (fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)) < 0) |
|
2400 goto no_setsockopt; |
|
2401 break; |
|
2402 case AF_INET6: |
|
2403 #ifdef IPV6_TCLASS |
|
2404 if (SETSOCKOPT (fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof (tos)) < 0) |
|
2405 goto no_setsockopt; |
|
2406 break; |
|
2407 #endif |
|
2408 default: |
|
2409 goto wrong_family; |
|
2410 } |
|
2411 |
|
2412 return GST_RTSP_OK; |
|
2413 |
|
2414 /* ERRORS */ |
|
2415 no_getsockname: |
|
2416 no_setsockopt: |
|
2417 { |
|
2418 return GST_RTSP_ESYS; |
|
2419 } |
|
2420 |
|
2421 wrong_family: |
|
2422 { |
|
2423 return GST_RTSP_ERROR; |
|
2424 } |
|
2425 } |
|
2426 |
|
2427 /** |
|
2428 * gst_rtsp_connection_set_qos_dscp: |
|
2429 * @conn: a #GstRTSPConnection |
|
2430 * @qos_dscp: DSCP value |
|
2431 * |
|
2432 * Configure @conn to use the specified DSCP value. |
|
2433 * |
|
2434 * Returns: #GST_RTSP_OK on success. |
|
2435 * |
|
2436 * Since: 0.10.20 |
|
2437 */ |
|
2438 GstRTSPResult |
|
2439 gst_rtsp_connection_set_qos_dscp (GstRTSPConnection * conn, guint qos_dscp) |
|
2440 { |
|
2441 GstRTSPResult res; |
|
2442 |
|
2443 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); |
|
2444 g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL); |
|
2445 g_return_val_if_fail (conn->writefd != NULL, GST_RTSP_EINVAL); |
|
2446 |
|
2447 res = set_qos_dscp (conn->fd0.fd, qos_dscp); |
|
2448 if (res == GST_RTSP_OK) |
|
2449 res = set_qos_dscp (conn->fd1.fd, qos_dscp); |
|
2450 |
|
2451 return res; |
|
2452 } |
|
2453 |
|
2454 |
|
2455 /** |
|
2456 * gst_rtsp_connection_get_url: |
|
2457 * @conn: a #GstRTSPConnection |
|
2458 * |
|
2459 * Retrieve the URL of the other end of @conn. |
|
2460 * |
|
2461 * Returns: The URL. This value remains valid until the |
|
2462 * connection is freed. |
|
2463 * |
|
2464 * Since: 0.10.23 |
|
2465 */ |
|
2466 GstRTSPUrl * |
|
2467 gst_rtsp_connection_get_url (const GstRTSPConnection * conn) |
|
2468 { |
|
2469 g_return_val_if_fail (conn != NULL, NULL); |
|
2470 |
|
2471 return conn->url; |
|
2472 } |
|
2473 |
|
2474 /** |
|
2475 * gst_rtsp_connection_get_ip: |
|
2476 * @conn: a #GstRTSPConnection |
|
2477 * |
|
2478 * Retrieve the IP address of the other end of @conn. |
|
2479 * |
|
2480 * Returns: The IP address as a string. this value remains valid until the |
|
2481 * connection is closed. |
|
2482 * |
|
2483 * Since: 0.10.20 |
|
2484 */ |
|
2485 const gchar * |
|
2486 gst_rtsp_connection_get_ip (const GstRTSPConnection * conn) |
|
2487 { |
|
2488 g_return_val_if_fail (conn != NULL, NULL); |
|
2489 |
|
2490 return conn->ip; |
|
2491 } |
|
2492 |
|
2493 /** |
|
2494 * gst_rtsp_connection_set_ip: |
|
2495 * @conn: a #GstRTSPConnection |
|
2496 * @ip: an ip address |
|
2497 * |
|
2498 * Set the IP address of the server. |
|
2499 * |
|
2500 * Since: 0.10.23 |
|
2501 */ |
|
2502 void |
|
2503 gst_rtsp_connection_set_ip (GstRTSPConnection * conn, const gchar * ip) |
|
2504 { |
|
2505 g_return_if_fail (conn != NULL); |
|
2506 |
|
2507 g_free (conn->ip); |
|
2508 conn->ip = g_strdup (ip); |
|
2509 } |
|
2510 |
|
2511 /** |
|
2512 * gst_rtsp_connection_get_readfd: |
|
2513 * @conn: a #GstRTSPConnection |
|
2514 * |
|
2515 * Get the file descriptor for reading. |
|
2516 * |
|
2517 * Returns: the file descriptor used for reading or -1 on error. The file |
|
2518 * descriptor remains valid until the connection is closed. |
|
2519 * |
|
2520 * Since: 0.10.23 |
|
2521 */ |
|
2522 gint |
|
2523 gst_rtsp_connection_get_readfd (const GstRTSPConnection * conn) |
|
2524 { |
|
2525 g_return_val_if_fail (conn != NULL, -1); |
|
2526 g_return_val_if_fail (conn->readfd != NULL, -1); |
|
2527 |
|
2528 return conn->readfd->fd; |
|
2529 } |
|
2530 |
|
2531 /** |
|
2532 * gst_rtsp_connection_get_writefd: |
|
2533 * @conn: a #GstRTSPConnection |
|
2534 * |
|
2535 * Get the file descriptor for writing. |
|
2536 * |
|
2537 * Returns: the file descriptor used for writing or -1 on error. The file |
|
2538 * descriptor remains valid until the connection is closed. |
|
2539 * |
|
2540 * Since: 0.10.23 |
|
2541 */ |
|
2542 gint |
|
2543 gst_rtsp_connection_get_writefd (const GstRTSPConnection * conn) |
|
2544 { |
|
2545 g_return_val_if_fail (conn != NULL, -1); |
|
2546 g_return_val_if_fail (conn->writefd != NULL, -1); |
|
2547 |
|
2548 return conn->writefd->fd; |
|
2549 } |
|
2550 |
|
2551 |
|
2552 /** |
|
2553 * gst_rtsp_connection_set_tunneled: |
|
2554 * @conn: a #GstRTSPConnection |
|
2555 * @tunneled: the new state |
|
2556 * |
|
2557 * Set the HTTP tunneling state of the connection. This must be configured before |
|
2558 * the @conn is connected. |
|
2559 * |
|
2560 * Since: 0.10.23 |
|
2561 */ |
|
2562 void |
|
2563 gst_rtsp_connection_set_tunneled (GstRTSPConnection * conn, gboolean tunneled) |
|
2564 { |
|
2565 g_return_if_fail (conn != NULL); |
|
2566 g_return_if_fail (conn->readfd == NULL); |
|
2567 g_return_if_fail (conn->writefd == NULL); |
|
2568 |
|
2569 conn->tunneled = tunneled; |
|
2570 } |
|
2571 |
|
2572 /** |
|
2573 * gst_rtsp_connection_is_tunneled: |
|
2574 * @conn: a #GstRTSPConnection |
|
2575 * |
|
2576 * Get the tunneling state of the connection. |
|
2577 * |
|
2578 * Returns: if @conn is using HTTP tunneling. |
|
2579 * |
|
2580 * Since: 0.10.23 |
|
2581 */ |
|
2582 gboolean |
|
2583 gst_rtsp_connection_is_tunneled (const GstRTSPConnection * conn) |
|
2584 { |
|
2585 g_return_val_if_fail (conn != NULL, FALSE); |
|
2586 |
|
2587 return conn->tunneled; |
|
2588 } |
|
2589 |
|
2590 /** |
|
2591 * gst_rtsp_connection_get_tunnelid: |
|
2592 * @conn: a #GstRTSPConnection |
|
2593 * |
|
2594 * Get the tunnel session id the connection. |
|
2595 * |
|
2596 * Returns: returns a non-empty string if @conn is being tunneled over HTTP. |
|
2597 * |
|
2598 * Since: 0.10.23 |
|
2599 */ |
|
2600 const gchar * |
|
2601 gst_rtsp_connection_get_tunnelid (const GstRTSPConnection * conn) |
|
2602 { |
|
2603 g_return_val_if_fail (conn != NULL, NULL); |
|
2604 |
|
2605 if (!conn->tunneled) |
|
2606 return NULL; |
|
2607 |
|
2608 return conn->tunnelid; |
|
2609 } |
|
2610 |
|
2611 /** |
|
2612 * gst_rtsp_connection_do_tunnel: |
|
2613 * @conn: a #GstRTSPConnection |
|
2614 * @conn2: a #GstRTSPConnection |
|
2615 * |
|
2616 * If @conn received the first tunnel connection and @conn2 received |
|
2617 * the second tunnel connection, link the two connections together so that |
|
2618 * @conn manages the tunneled connection. |
|
2619 * |
|
2620 * After this call, @conn2 cannot be used anymore and must be freed with |
|
2621 * gst_rtsp_connection_free(). |
|
2622 * |
|
2623 * Returns: return GST_RTSP_OK on success. |
|
2624 * |
|
2625 * Since: 0.10.23 |
|
2626 */ |
|
2627 GstRTSPResult |
|
2628 gst_rtsp_connection_do_tunnel (GstRTSPConnection * conn, |
|
2629 GstRTSPConnection * conn2) |
|
2630 { |
|
2631 g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); |
|
2632 g_return_val_if_fail (conn2 != NULL, GST_RTSP_EINVAL); |
|
2633 g_return_val_if_fail (conn->tstate == TUNNEL_STATE_GET, GST_RTSP_EINVAL); |
|
2634 g_return_val_if_fail (conn2->tstate == TUNNEL_STATE_POST, GST_RTSP_EINVAL); |
|
2635 g_return_val_if_fail (!memcmp (conn2->tunnelid, conn->tunnelid, TUNNELID_LEN), |
|
2636 GST_RTSP_EINVAL); |
|
2637 |
|
2638 /* both connections have fd0 as the read/write socket. start by taking the |
|
2639 * socket from conn2 and set it as the socket in conn */ |
|
2640 conn->fd1 = conn2->fd0; |
|
2641 |
|
2642 /* clean up some of the state of conn2 */ |
|
2643 gst_poll_remove_fd (conn2->fdset, &conn2->fd0); |
|
2644 conn2->fd0.fd = -1; |
|
2645 conn2->readfd = conn2->writefd = NULL; |
|
2646 |
|
2647 /* We make fd0 the write socket and fd1 the read socket. */ |
|
2648 conn->writefd = &conn->fd0; |
|
2649 conn->readfd = &conn->fd1; |
|
2650 |
|
2651 conn->tstate = TUNNEL_STATE_COMPLETE; |
|
2652 |
|
2653 /* we need base64 decoding for the readfd */ |
|
2654 conn->ctx.state = 0; |
|
2655 conn->ctx.save = 0; |
|
2656 conn->ctx.cout = 0; |
|
2657 conn->ctx.coutl = 0; |
|
2658 conn->ctxp = &conn->ctx; |
|
2659 |
|
2660 return GST_RTSP_OK; |
|
2661 } |
|
2662 |
|
2663 #define READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) |
|
2664 #define WRITE_COND (G_IO_OUT | G_IO_ERR) |
|
2665 |
|
2666 typedef struct |
|
2667 { |
|
2668 guint8 *data; |
|
2669 guint size; |
|
2670 guint id; |
|
2671 } GstRTSPRec; |
|
2672 |
|
2673 /* async functions */ |
|
2674 struct _GstRTSPWatch |
|
2675 { |
|
2676 GSource source; |
|
2677 |
|
2678 GstRTSPConnection *conn; |
|
2679 |
|
2680 GstRTSPBuilder builder; |
|
2681 GstRTSPMessage message; |
|
2682 |
|
2683 GPollFD readfd; |
|
2684 GPollFD writefd; |
|
2685 gboolean write_added; |
|
2686 |
|
2687 /* queued message for transmission */ |
|
2688 guint id; |
|
2689 GAsyncQueue *messages; |
|
2690 guint8 *write_data; |
|
2691 guint write_off; |
|
2692 guint write_size; |
|
2693 guint write_id; |
|
2694 |
|
2695 GstRTSPWatchFuncs funcs; |
|
2696 |
|
2697 gpointer user_data; |
|
2698 GDestroyNotify notify; |
|
2699 }; |
|
2700 |
|
2701 static gboolean |
|
2702 gst_rtsp_source_prepare (GSource * source, gint * timeout) |
|
2703 { |
|
2704 GstRTSPWatch *watch = (GstRTSPWatch *) source; |
|
2705 |
|
2706 *timeout = (watch->conn->timeout * 1000); |
|
2707 |
|
2708 return FALSE; |
|
2709 } |
|
2710 |
|
2711 static gboolean |
|
2712 gst_rtsp_source_check (GSource * source) |
|
2713 { |
|
2714 GstRTSPWatch *watch = (GstRTSPWatch *) source; |
|
2715 |
|
2716 if (watch->readfd.revents & READ_COND) |
|
2717 return TRUE; |
|
2718 |
|
2719 if (watch->writefd.revents & WRITE_COND) |
|
2720 return TRUE; |
|
2721 |
|
2722 return FALSE; |
|
2723 } |
|
2724 |
|
2725 static gboolean |
|
2726 gst_rtsp_source_dispatch (GSource * source, GSourceFunc callback G_GNUC_UNUSED, |
|
2727 gpointer user_data G_GNUC_UNUSED) |
|
2728 { |
|
2729 GstRTSPWatch *watch = (GstRTSPWatch *) source; |
|
2730 GstRTSPResult res; |
|
2731 |
|
2732 /* first read as much as we can */ |
|
2733 if (watch->readfd.revents & READ_COND) { |
|
2734 do { |
|
2735 res = build_next (&watch->builder, &watch->message, watch->conn); |
|
2736 if (res == GST_RTSP_EINTR) |
|
2737 break; |
|
2738 if (G_UNLIKELY (res == GST_RTSP_EEOF)) |
|
2739 goto eof; |
|
2740 if (res == GST_RTSP_ETGET) { |
|
2741 GString *str; |
|
2742 GstRTSPStatusCode code; |
|
2743 guint size; |
|
2744 |
|
2745 if (watch->funcs.tunnel_start) |
|
2746 code = watch->funcs.tunnel_start (watch, watch->user_data); |
|
2747 else |
|
2748 code = GST_RTSP_STS_OK; |
|
2749 |
|
2750 /* queue the response string */ |
|
2751 str = gen_tunnel_reply (watch->conn, code); |
|
2752 size = str->len; |
|
2753 gst_rtsp_watch_queue_data (watch, (guint8 *) g_string_free (str, FALSE), |
|
2754 size); |
|
2755 } else if (res == GST_RTSP_ETPOST) { |
|
2756 /* in the callback the connection should be tunneled with the |
|
2757 * GET connection */ |
|
2758 if (watch->funcs.tunnel_complete) |
|
2759 watch->funcs.tunnel_complete (watch, watch->user_data); |
|
2760 } else if (G_UNLIKELY (res != GST_RTSP_OK)) |
|
2761 goto error; |
|
2762 |
|
2763 if (G_LIKELY (res == GST_RTSP_OK)) { |
|
2764 if (watch->funcs.message_received) |
|
2765 watch->funcs.message_received (watch, &watch->message, |
|
2766 watch->user_data); |
|
2767 |
|
2768 gst_rtsp_message_unset (&watch->message); |
|
2769 } |
|
2770 build_reset (&watch->builder); |
|
2771 } while (FALSE); |
|
2772 } |
|
2773 |
|
2774 if (watch->writefd.revents & WRITE_COND) { |
|
2775 do { |
|
2776 if (watch->write_data == NULL) { |
|
2777 GstRTSPRec *rec; |
|
2778 |
|
2779 /* get a new message from the queue */ |
|
2780 rec = g_async_queue_try_pop (watch->messages); |
|
2781 if (rec == NULL) |
|
2782 goto done; |
|
2783 |
|
2784 watch->write_off = 0; |
|
2785 watch->write_data = rec->data; |
|
2786 watch->write_size = rec->size; |
|
2787 watch->write_id = rec->id; |
|
2788 |
|
2789 g_slice_free (GstRTSPRec, rec); |
|
2790 } |
|
2791 |
|
2792 res = write_bytes (watch->writefd.fd, watch->write_data, |
|
2793 &watch->write_off, watch->write_size); |
|
2794 if (res == GST_RTSP_EINTR) |
|
2795 break; |
|
2796 if (G_UNLIKELY (res != GST_RTSP_OK)) |
|
2797 goto error; |
|
2798 |
|
2799 if (watch->funcs.message_sent) |
|
2800 watch->funcs.message_sent (watch, watch->write_id, watch->user_data); |
|
2801 |
|
2802 done: |
|
2803 if (g_async_queue_length (watch->messages) == 0 && watch->write_added) { |
|
2804 g_source_remove_poll ((GSource *) watch, &watch->writefd); |
|
2805 watch->write_added = FALSE; |
|
2806 watch->writefd.revents = 0; |
|
2807 } |
|
2808 g_free (watch->write_data); |
|
2809 watch->write_data = NULL; |
|
2810 } while (FALSE); |
|
2811 } |
|
2812 |
|
2813 return TRUE; |
|
2814 |
|
2815 /* ERRORS */ |
|
2816 eof: |
|
2817 { |
|
2818 if (watch->funcs.closed) |
|
2819 watch->funcs.closed (watch, watch->user_data); |
|
2820 return FALSE; |
|
2821 } |
|
2822 error: |
|
2823 { |
|
2824 if (watch->funcs.error) |
|
2825 watch->funcs.error (watch, res, watch->user_data); |
|
2826 return FALSE; |
|
2827 } |
|
2828 } |
|
2829 |
|
2830 static void |
|
2831 gst_rtsp_rec_free (gpointer data) |
|
2832 { |
|
2833 GstRTSPRec *rec = data; |
|
2834 |
|
2835 g_free (rec->data); |
|
2836 g_slice_free (GstRTSPRec, rec); |
|
2837 } |
|
2838 |
|
2839 static void |
|
2840 gst_rtsp_source_finalize (GSource * source) |
|
2841 { |
|
2842 GstRTSPWatch *watch = (GstRTSPWatch *) source; |
|
2843 |
|
2844 build_reset (&watch->builder); |
|
2845 gst_rtsp_message_unset (&watch->message); |
|
2846 |
|
2847 g_async_queue_unref (watch->messages); |
|
2848 watch->messages = NULL; |
|
2849 |
|
2850 g_free (watch->write_data); |
|
2851 |
|
2852 if (watch->notify) |
|
2853 watch->notify (watch->user_data); |
|
2854 } |
|
2855 |
|
2856 static GSourceFuncs gst_rtsp_source_funcs = { |
|
2857 gst_rtsp_source_prepare, |
|
2858 gst_rtsp_source_check, |
|
2859 gst_rtsp_source_dispatch, |
|
2860 gst_rtsp_source_finalize, |
|
2861 NULL, |
|
2862 NULL |
|
2863 }; |
|
2864 |
|
2865 /** |
|
2866 * gst_rtsp_watch_new: |
|
2867 * @conn: a #GstRTSPConnection |
|
2868 * @funcs: watch functions |
|
2869 * @user_data: user data to pass to @funcs |
|
2870 * @notify: notify when @user_data is not referenced anymore |
|
2871 * |
|
2872 * Create a watch object for @conn. The functions provided in @funcs will be |
|
2873 * called with @user_data when activity happened on the watch. |
|
2874 * |
|
2875 * The new watch is usually created so that it can be attached to a |
|
2876 * maincontext with gst_rtsp_watch_attach(). |
|
2877 * |
|
2878 * @conn must exist for the entire lifetime of the watch. |
|
2879 * |
|
2880 * Returns: a #GstRTSPWatch that can be used for asynchronous RTSP |
|
2881 * communication. Free with gst_rtsp_watch_unref () after usage. |
|
2882 * |
|
2883 * Since: 0.10.23 |
|
2884 */ |
|
2885 GstRTSPWatch * |
|
2886 gst_rtsp_watch_new (GstRTSPConnection * conn, |
|
2887 GstRTSPWatchFuncs * funcs, gpointer user_data, GDestroyNotify notify) |
|
2888 { |
|
2889 GstRTSPWatch *result; |
|
2890 |
|
2891 g_return_val_if_fail (conn != NULL, NULL); |
|
2892 g_return_val_if_fail (funcs != NULL, NULL); |
|
2893 g_return_val_if_fail (conn->readfd != NULL, NULL); |
|
2894 g_return_val_if_fail (conn->writefd != NULL, NULL); |
|
2895 |
|
2896 result = (GstRTSPWatch *) g_source_new (&gst_rtsp_source_funcs, |
|
2897 sizeof (GstRTSPWatch)); |
|
2898 |
|
2899 result->conn = conn; |
|
2900 result->builder.state = STATE_START; |
|
2901 |
|
2902 result->messages = g_async_queue_new_full (gst_rtsp_rec_free); |
|
2903 |
|
2904 result->readfd.fd = -1; |
|
2905 result->writefd.fd = -1; |
|
2906 |
|
2907 gst_rtsp_watch_reset (result); |
|
2908 |
|
2909 result->funcs = *funcs; |
|
2910 result->user_data = user_data; |
|
2911 result->notify = notify; |
|
2912 |
|
2913 /* only add the read fd, the write fd is only added when we have data |
|
2914 * to send. */ |
|
2915 g_source_add_poll ((GSource *) result, &result->readfd); |
|
2916 |
|
2917 return result; |
|
2918 } |
|
2919 |
|
2920 /** |
|
2921 * gst_rtsp_watch_reset: |
|
2922 * @watch: a #GstRTSPWatch |
|
2923 * |
|
2924 * Reset @watch, this is usually called after gst_rtsp_connection_do_tunnel() |
|
2925 * when the file descriptors of the connection might have changed. |
|
2926 * |
|
2927 * Since: 0.10.23 |
|
2928 */ |
|
2929 void |
|
2930 gst_rtsp_watch_reset (GstRTSPWatch * watch) |
|
2931 { |
|
2932 if (watch->readfd.fd != -1) |
|
2933 g_source_remove_poll ((GSource *) watch, &watch->readfd); |
|
2934 if (watch->writefd.fd != -1) |
|
2935 g_source_remove_poll ((GSource *) watch, &watch->writefd); |
|
2936 |
|
2937 watch->readfd.fd = watch->conn->readfd->fd; |
|
2938 watch->readfd.events = READ_COND; |
|
2939 watch->readfd.revents = 0; |
|
2940 |
|
2941 watch->writefd.fd = watch->conn->writefd->fd; |
|
2942 watch->writefd.events = WRITE_COND; |
|
2943 watch->writefd.revents = 0; |
|
2944 watch->write_added = FALSE; |
|
2945 |
|
2946 g_source_add_poll ((GSource *) watch, &watch->readfd); |
|
2947 } |
|
2948 |
|
2949 /** |
|
2950 * gst_rtsp_watch_attach: |
|
2951 * @watch: a #GstRTSPWatch |
|
2952 * @context: a GMainContext (if NULL, the default context will be used) |
|
2953 * |
|
2954 * Adds a #GstRTSPWatch to a context so that it will be executed within that context. |
|
2955 * |
|
2956 * Returns: the ID (greater than 0) for the watch within the GMainContext. |
|
2957 * |
|
2958 * Since: 0.10.23 |
|
2959 */ |
|
2960 guint |
|
2961 gst_rtsp_watch_attach (GstRTSPWatch * watch, GMainContext * context) |
|
2962 { |
|
2963 g_return_val_if_fail (watch != NULL, 0); |
|
2964 |
|
2965 return g_source_attach ((GSource *) watch, context); |
|
2966 } |
|
2967 |
|
2968 /** |
|
2969 * gst_rtsp_watch_unref: |
|
2970 * @watch: a #GstRTSPWatch |
|
2971 * |
|
2972 * Decreases the reference count of @watch by one. If the resulting reference |
|
2973 * count is zero the watch and associated memory will be destroyed. |
|
2974 * |
|
2975 * Since: 0.10.23 |
|
2976 */ |
|
2977 void |
|
2978 gst_rtsp_watch_unref (GstRTSPWatch * watch) |
|
2979 { |
|
2980 g_return_if_fail (watch != NULL); |
|
2981 |
|
2982 g_source_unref ((GSource *) watch); |
|
2983 } |
|
2984 |
|
2985 /** |
|
2986 * gst_rtsp_watch_queue_data: |
|
2987 * @watch: a #GstRTSPWatch |
|
2988 * @data: the data to queue |
|
2989 * @size: the size of @data |
|
2990 * |
|
2991 * Queue @data for transmission in @watch. It will be transmitted when the |
|
2992 * connection of the @watch becomes writable. |
|
2993 * |
|
2994 * This function will take ownership of @data and g_free() it after use. |
|
2995 * |
|
2996 * The return value of this function will be used as the id argument in the |
|
2997 * message_sent callback. |
|
2998 * |
|
2999 * Returns: an id. |
|
3000 * |
|
3001 * Since: 0.10.24 |
|
3002 */ |
|
3003 guint |
|
3004 gst_rtsp_watch_queue_data (GstRTSPWatch * watch, const guint8 * data, |
|
3005 guint size) |
|
3006 { |
|
3007 GstRTSPRec *rec; |
|
3008 |
|
3009 g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL); |
|
3010 g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL); |
|
3011 g_return_val_if_fail (size != 0, GST_RTSP_EINVAL); |
|
3012 |
|
3013 /* make a record with the data and id */ |
|
3014 rec = g_slice_new (GstRTSPRec); |
|
3015 rec->data = (guint8 *) data; |
|
3016 rec->size = size; |
|
3017 do { |
|
3018 /* make sure rec->id is never 0 */ |
|
3019 rec->id = ++watch->id; |
|
3020 } while (G_UNLIKELY (rec->id == 0)); |
|
3021 |
|
3022 /* add the record to a queue. FIXME we would like to have an upper limit here */ |
|
3023 g_async_queue_push (watch->messages, rec); |
|
3024 |
|
3025 /* FIXME: does the following need to be made thread-safe? (this might be |
|
3026 * called from a streaming thread, like appsink's render function) */ |
|
3027 /* make sure the main context will now also check for writability on the |
|
3028 * socket */ |
|
3029 if (!watch->write_added) { |
|
3030 g_source_add_poll ((GSource *) watch, &watch->writefd); |
|
3031 watch->write_added = TRUE; |
|
3032 } |
|
3033 |
|
3034 return rec->id; |
|
3035 } |
|
3036 |
|
3037 /** |
|
3038 * gst_rtsp_watch_queue_message: |
|
3039 * @watch: a #GstRTSPWatch |
|
3040 * @message: a #GstRTSPMessage |
|
3041 * |
|
3042 * Queue a @message for transmission in @watch. The contents of this |
|
3043 * message will be serialized and transmitted when the connection of the |
|
3044 * @watch becomes writable. |
|
3045 * |
|
3046 * The return value of this function will be used as the id argument in the |
|
3047 * message_sent callback. |
|
3048 * |
|
3049 * Returns: an id. |
|
3050 * |
|
3051 * Since: 0.10.23 |
|
3052 */ |
|
3053 guint |
|
3054 gst_rtsp_watch_queue_message (GstRTSPWatch * watch, GstRTSPMessage * message) |
|
3055 { |
|
3056 GString *str; |
|
3057 guint size; |
|
3058 |
|
3059 g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL); |
|
3060 g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL); |
|
3061 |
|
3062 /* make a record with the message as a string and id */ |
|
3063 str = message_to_string (watch->conn, message); |
|
3064 size = str->len; |
|
3065 return gst_rtsp_watch_queue_data (watch, |
|
3066 (guint8 *) g_string_free (str, FALSE), size); |
|
3067 } |