diff options
| -rw-r--r-- | ChangeLog | 21 | ||||
| -rw-r--r-- | gst/rtsp/gstrtspsrc.c | 258 | ||||
| -rw-r--r-- | gst/rtsp/gstrtspsrc.h | 2 | ||||
| -rw-r--r-- | gst/rtsp/rtspconnection.c | 62 | ||||
| -rw-r--r-- | gst/rtsp/rtspconnection.h | 9 | ||||
| -rw-r--r-- | gst/rtsp/rtspdefs.h | 10 | ||||
| -rw-r--r-- | gst/rtsp/rtspurl.c | 17 | ||||
| -rw-r--r-- | gst/rtsp/rtspurl.h | 1 | 
8 files changed, 343 insertions, 37 deletions
@@ -1,3 +1,24 @@ +2007-02-23  Jan Schmidt  <thaytan@mad.scientist.com> + +	* gst/rtsp/gstrtspsrc.c: (gst_rtspsrc_finalize), +	(gst_rtspsrc_create_stream), (rtsp_auth_method_to_string), +	(gst_rtspsrc_parse_auth_hdr), (gst_rtspsrc_setup_auth), +	(gst_rtspsrc_send), (gst_rtspsrc_try_send), (gst_rtspsrc_open), +	(gst_rtspsrc_close), (gst_rtspsrc_play), (gst_rtspsrc_pause), +	(gst_rtspsrc_uri_set_uri): +	* gst/rtsp/gstrtspsrc.h: +	* gst/rtsp/rtspconnection.c: (rtsp_connection_create), +	(append_auth_header), (rtsp_connection_send), +	(rtsp_connection_free), (rtsp_connection_set_auth): +	* gst/rtsp/rtspconnection.h: +	* gst/rtsp/rtspdefs.h: +	* gst/rtsp/rtspurl.c: (rtsp_url_get_request_uri): +	* gst/rtsp/rtspurl.h: + +	Implement simple Basic Authentication support so that urls like +	rtsp://user:pass@hostname/rtspstream work on hosts that require +	authentication. +  2007-02-22  Edgard Lima <edgard.lima@indt.org.br>  	* sys/v4l2/gstv4l2object.c: diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index ffbf005e..eb85370d 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -177,6 +177,12 @@ static GstStateChangeReturn gst_rtspsrc_change_state (GstElement * element,      GstStateChange transition);  static void gst_rtspsrc_handle_message (GstBin * bin, GstMessage * message); +static gboolean gst_rtspsrc_setup_auth (GstRTSPSrc * src, +    RTSPMessage * response); +static gboolean gst_rtspsrc_try_send (GstRTSPSrc * src, RTSPMessage * request, +    RTSPMessage * response, RTSPStatusCode * code); + +  static gboolean gst_rtspsrc_open (GstRTSPSrc * src);  static gboolean gst_rtspsrc_play (GstRTSPSrc * src);  static gboolean gst_rtspsrc_pause (GstRTSPSrc * src); @@ -301,6 +307,7 @@ gst_rtspsrc_finalize (GObject * object)    g_free (rtspsrc->stream_rec_lock);    g_cond_free (rtspsrc->loop_cond);    g_free (rtspsrc->location); +  g_free (rtspsrc->req_location);    g_free (rtspsrc->content_base);    rtsp_url_free (rtspsrc->url); @@ -439,7 +446,8 @@ gst_rtspsrc_create_stream (GstRTSPSrc * src, SDPMessage * sdp, gint idx)        stream->setup_url =            g_strdup_printf ("%s%s", src->content_base, control_url);      else -      stream->setup_url = g_strdup_printf ("%s/%s", src->location, control_url); +      stream->setup_url = +          g_strdup_printf ("%s/%s", src->req_location, control_url);    }    GST_DEBUG_OBJECT (src, " setup: %s", GST_STR_NULL (stream->setup_url)); @@ -1533,6 +1541,145 @@ send_error:    }  } +#ifndef GST_DISABLE_GST_DEBUG +const gchar * +rtsp_auth_method_to_string (RTSPAuthMethod method) +{ +  gint index = 0; + +  while (method != 0) { +    index++; +    method >>= 1; +  } +  switch (index) { +    case 0: +      return "None"; +    case 1: +      return "Basic"; +    case 2: +      return "Digest"; +  } + +  return "Unknown"; +} +#endif + +/* Parse a WWW-Authenticate Response header and determine the  + * available authentication methods + * FIXME: To implement digest or other auth types, we should extract + * the authentication tokens that the server provided for each method + * into an array of structures and give those to the connection object. + * + * This code should also cope with the fact that each WWW-Authenticate + * header can contain multiple challenge methods + tokens  + * + * At the moment, we just do a minimal check for Basic auth and don't + * even parse out the realm */ +static void +gst_rtspsrc_parse_auth_hdr (gchar * hdr, RTSPAuthMethod * methods) +{ +  gchar *start; + +  g_return_if_fail (hdr != NULL); +  g_return_if_fail (methods != NULL); + +  /* Skip whitespace at the start of the string */ +  for (start = hdr; start[0] != '\0' && g_ascii_isspace (start[0]); start++); + +  if (g_ascii_strncasecmp (start, "basic", 5) == 0) +    *methods |= RTSP_AUTH_BASIC; +} + +/** + * gst_rtspsrc_setup_auth: + * @src: the rtsp source + * + * Configure a username and password and auth method on the  + * connection object based on a response we received from the  + * peer. + * + * Currently, this requires that a username and password were supplied + * in the uri. In the future, they may be requested on demand by sending + * a message up the bus. + * + * Returns: TRUE if authentication information could be set up correctly. + */ +static gboolean +gst_rtspsrc_setup_auth (GstRTSPSrc * src, RTSPMessage * response) +{ +  gchar *user = NULL; +  gchar *pass = NULL; +  RTSPAuthMethod avail_methods = RTSP_AUTH_NONE; +  RTSPAuthMethod method; +  RTSPResult auth_result; +  gchar *hdr; + +  /* Identify the available auth methods and see if any are supported */ +  if (rtsp_message_get_header (response, RTSP_HDR_WWW_AUTHENTICATE, &hdr) == +      RTSP_OK) { +    gst_rtspsrc_parse_auth_hdr (hdr, &avail_methods); +  } + +  if (avail_methods == RTSP_AUTH_NONE) +    goto no_auth_available; + +  /* FIXME: For digest auth, if the response indicates that the session +   * data are stale, we just update them in the connection object and +   * return TRUE to retry the request */ + +  /* Do we have username and password available? */ +  if (src->url != NULL && !src->tried_url_auth) { +    user = src->url->user; +    pass = src->url->passwd; +    src->tried_url_auth = TRUE; +    GST_DEBUG_OBJECT (src, +        "Attempting authentication using credentials from the URL"); +  } + +  /* FIXME: If the url didn't contain username and password or we tried them +   * already, request a username and passwd from the application via some kind +   * of credentials request message */ + +  /* If we don't have a username and passwd at this point, bail out. */ +  if (user == NULL || pass == NULL) +    goto no_user_pass; + +  /* Try to configure for each available authentication method, strongest to +   * weakest */ +  for (method = RTSP_AUTH_MAX; method != RTSP_AUTH_NONE; method >>= 1) { +    /* Check if this method is available on the server */ +    if ((method & avail_methods) == 0) +      continue; + +    /* Pass the credentials to the connection to try on the next request */ +    auth_result = +        rtsp_connection_set_auth (src->connection, method, user, pass); +    /* INVAL indicates an invalid username/passwd were supplied, so we'll just +     * ignore it and end up retrying later */ +    if (auth_result == RTSP_OK || auth_result == RTSP_EINVAL) { +      GST_DEBUG_OBJECT (src, "Attempting %s authentication", +          rtsp_auth_method_to_string (method)); +      break; +    } +  } + +  if (method == RTSP_AUTH_NONE) +    goto no_auth_available; + +  return TRUE; + +no_auth_available: +  /* Output an error indicating that we couldn't connect because there were +   * no supported authentication protocols */ +  GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), +      ("No supported authentication protocol was found")); +  return FALSE; +no_user_pass: +  /* We don't fire an error message, we just return FALSE and let the +   * normal NOT_AUTHORIZED error be propagated */ +  return FALSE; +} +  /**   * gst_rtspsrc_send:   * @src: the rtsp source @@ -1548,12 +1695,65 @@ send_error:   * If @code is NULL, this function will return FALSE (with an invalid @response   * message) if the response code was not 200 (OK).   * + * If the attempt results in an authentication failure, then this will attempt + * to retrieve authentication credentials via gst_rtspsrc_setup_auth and retry + * the request. + *   * Returns: TRUE if the processing was successful.   */  gboolean  gst_rtspsrc_send (GstRTSPSrc * src, RTSPMessage * request,      RTSPMessage * response, RTSPStatusCode * code)  { +  RTSPStatusCode int_code = RTSP_STS_OK; +  gboolean res; +  gboolean retry; + +  do { +    retry = FALSE; +    res = gst_rtspsrc_try_send (src, request, response, &int_code); + +    if (int_code == RTSP_STS_UNAUTHORIZED) { +      if (gst_rtspsrc_setup_auth (src, response)) { +        /* Try the request/response again after configuring the auth info +         * and loop again */ +        retry = TRUE; +      } +    } +  } while (retry == TRUE); + +  /* If the user requested the code, let them handle errors, otherwise +   * post an error below */ +  if (code != NULL) +    *code = int_code; +  else if (int_code != RTSP_STS_OK) +    goto error_response; + +  return res; + +error_response: +  { +    switch (response->type_data.response.code) { +      case RTSP_STS_NOT_FOUND: +        GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), ("%s", +                response->type_data.response.reason)); +        break; +      default: +        GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), +            ("Got error response: %d (%s).", response->type_data.response.code, +                response->type_data.response.reason)); +        break; +    } +    /* we return FALSE so we should unset the response ourselves */ +    rtsp_message_unset (response); +    return FALSE; +  } +} + +static gboolean +gst_rtspsrc_try_send (GstRTSPSrc * src, RTSPMessage * request, +    RTSPMessage * response, RTSPStatusCode * code) +{    RTSPResult res;    RTSPStatusCode thecode;    gchar *content_base = NULL; @@ -1590,12 +1790,14 @@ next:    }    thecode = response->type_data.response.code; -  /* if the caller wanted the result code, we store it. Else we check if it's -   * OK. */ + +  /* if the caller wanted the result code, we store it. */    if (code)      *code = thecode; -  else if (thecode != RTSP_STS_OK) -    goto error_response; + +  /* If the request didn't succeed, bail out before doing any more */ +  if (thecode != RTSP_STS_OK) +    return FALSE;    /* store new content base if any */    rtsp_message_get_header (response, RTSP_HDR_CONTENT_BASE, &content_base); @@ -1631,23 +1833,6 @@ handle_request_failed:      /* ERROR was posted */      return FALSE;    } -error_response: -  { -    switch (response->type_data.response.code) { -      case RTSP_STS_NOT_FOUND: -        GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), ("%s", -                response->type_data.response.reason)); -        break; -      default: -        GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), -            ("Got error response: %d (%s).", response->type_data.response.code, -                response->type_data.response.reason)); -        break; -    } -    /* we return FALSE so we should unset the response ourselves */ -    rtsp_message_unset (response); -    return FALSE; -  }  }  /* parse the response and collect all the supported methods. We need this @@ -2058,20 +2243,21 @@ gst_rtspsrc_open (GstRTSPSrc * src)    /* can't continue without a valid url */    if (G_UNLIKELY (src->url == NULL))      goto no_url; +  src->tried_url_auth = FALSE;    /* create connection */ -  GST_DEBUG_OBJECT (src, "creating connection (%s)...", src->location); +  GST_DEBUG_OBJECT (src, "creating connection (%s)...", src->req_location);    if ((res = rtsp_connection_create (src->url, &src->connection)) < 0)      goto could_not_create;    /* connect */ -  GST_DEBUG_OBJECT (src, "connecting (%s)...", src->location); +  GST_DEBUG_OBJECT (src, "connecting (%s)...", src->req_location);    if ((res = rtsp_connection_connect (src->connection)) < 0)      goto could_not_connect;    /* create OPTIONS */    GST_DEBUG_OBJECT (src, "create options..."); -  res = rtsp_message_init_request (&request, RTSP_OPTIONS, src->location); +  res = rtsp_message_init_request (&request, RTSP_OPTIONS, src->req_location);    if (res < 0)      goto create_request_failed; @@ -2086,7 +2272,7 @@ gst_rtspsrc_open (GstRTSPSrc * src)    /* create DESCRIBE */    GST_DEBUG_OBJECT (src, "create describe..."); -  res = rtsp_message_init_request (&request, RTSP_DESCRIBE, src->location); +  res = rtsp_message_init_request (&request, RTSP_DESCRIBE, src->req_location);    if (res < 0)      goto create_request_failed; @@ -2177,11 +2363,8 @@ create_request_failed:    }  send_error:    { -    gchar *str = rtsp_strresult (res); - -    GST_ELEMENT_ERROR (src, RESOURCE, WRITE, (NULL), -        ("Could not send message. (%s)", str)); -    g_free (str); +    /* Don't post a message - the rtsp_send method will have +     * taken care of it because we passed NULL for the response code */      goto cleanup_error;    }  methods_error: @@ -2232,7 +2415,8 @@ gst_rtspsrc_close (GstRTSPSrc * src)    if (src->methods & RTSP_PLAY) {      /* do TEARDOWN */ -    res = rtsp_message_init_request (&request, RTSP_TEARDOWN, src->location); +    res = +        rtsp_message_init_request (&request, RTSP_TEARDOWN, src->req_location);      if (res < 0)        goto create_request_failed; @@ -2317,7 +2501,7 @@ gst_rtspsrc_play (GstRTSPSrc * src)    GST_DEBUG_OBJECT (src, "PLAY...");    /* do play */ -  res = rtsp_message_init_request (&request, RTSP_PLAY, src->location); +  res = rtsp_message_init_request (&request, RTSP_PLAY, src->req_location);    if (res < 0)      goto create_request_failed; @@ -2378,7 +2562,7 @@ gst_rtspsrc_pause (GstRTSPSrc * src)    GST_DEBUG_OBJECT (src, "PAUSE...");    /* do pause */ -  res = rtsp_message_init_request (&request, RTSP_PAUSE, src->location); +  res = rtsp_message_init_request (&request, RTSP_PAUSE, src->req_location);    if (res < 0)      goto create_request_failed; @@ -2541,11 +2725,13 @@ gst_rtspsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)    rtsp_url_free (src->url);    src->url = newurl;    g_free (src->location); +  g_free (src->req_location);    src->location = g_strdup (uri); -  if (!g_str_has_prefix (src->location, "rtsp://")) -    memmove (src->location + 4, src->location + 5, strlen (src->location) - 4); +  src->req_location = rtsp_url_get_request_uri (src->url);    GST_DEBUG_OBJECT (src, "set uri: %s", GST_STR_NULL (uri)); +  GST_DEBUG_OBJECT (src, "request uri is: %s", +      GST_STR_NULL (src->req_location));    return TRUE; diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h index 8f42895f..e491d50a 100644 --- a/gst/rtsp/gstrtspsrc.h +++ b/gst/rtsp/gstrtspsrc.h @@ -130,6 +130,7 @@ struct _GstRTSPSrc {    /* properties */    gchar           *location; +  gchar           *req_location; /* Sanitised URL to use in network requests */    RTSPUrl         *url;    RTSPLowerTrans   protocols;    gboolean         debug; @@ -139,6 +140,7 @@ struct _GstRTSPSrc {    /* state */    gchar           *content_base;    RTSPLowerTrans   cur_protocols; +  gboolean         tried_url_auth;    /* supported methods */    gint             methods; diff --git a/gst/rtsp/rtspconnection.c b/gst/rtsp/rtspconnection.c index 1fb26bf5..19f5c6df 100644 --- a/gst/rtsp/rtspconnection.c +++ b/gst/rtsp/rtspconnection.c @@ -143,6 +143,10 @@ rtsp_connection_create (RTSPUrl * url, RTSPConnection ** conn)    newconn->session_id[0] = 0;    newconn->state = RTSP_STATE_INIT; +  newconn->auth_method = RTSP_AUTH_NONE; +  newconn->username = NULL; +  newconn->passwd = NULL; +    *conn = newconn;    return RTSP_OK; @@ -231,6 +235,30 @@ append_header (gint key, gchar * value, GString * str)    g_string_append_printf (str, "%s: %s\r\n", keystr, value);  } +static void +append_auth_header (RTSPConnection * conn, RTSPMessage * message, GString * str) +{ +  switch (conn->auth_method) { +    case RTSP_AUTH_BASIC:{ +      gchar *user_pass = +          g_strdup_printf ("%s:%s", conn->username, conn->passwd); +      gchar *user_pass64 = +          g_base64_encode ((guchar *) user_pass, strlen (user_pass)); +      gchar *auth_string = g_strdup_printf ("Basic %s", user_pass64); + +      append_header (RTSP_HDR_AUTHORIZATION, auth_string, str); + +      g_free (user_pass); +      g_free (user_pass64); +      g_free (auth_string); +      break; +    } +    default: +      /* Nothing to do */ +      break; +  } +} +  RTSPResult  rtsp_connection_send (RTSPConnection * conn, RTSPMessage * message)  { @@ -278,12 +306,15 @@ rtsp_connection_send (RTSPConnection * conn, RTSPMessage * message)    /* append session id if we have one */    if (conn->session_id[0] != '\0') { -    rtsp_message_add_header (message, RTSP_HDR_SESSION, conn->session_id); +    append_header (RTSP_HDR_SESSION, conn->session_id, str);    }    /* append headers */    g_hash_table_foreach (message->hdr_fields, (GHFunc) append_header, str); +  /* Append any authentication headers */ +  append_auth_header (conn, message, str); +    /* append Content-Length and body if needed */    if (message->body != NULL && message->body_size > 0) {      gchar *len; @@ -786,6 +817,9 @@ rtsp_connection_free (RTSPConnection * conn)    WSACleanup ();  #endif +  g_free (conn->username); +  g_free (conn->passwd); +    g_free (conn);    return RTSP_OK; @@ -812,3 +846,29 @@ rtsp_connection_flush (RTSPConnection * conn, gboolean flush)    }    return RTSP_OK;  } + +RTSPResult +rtsp_connection_set_auth (RTSPConnection * conn, RTSPAuthMethod method, +    gchar * user, gchar * pass) +{ +  /* Digest isn't implemented yet */ +  if (method == RTSP_AUTH_DIGEST) +    return RTSP_ENOTIMPL; + +  /* Make sure the username and passwd are being set for authentication */ +  if (method == RTSP_AUTH_NONE && (user == NULL || pass == NULL)) +    return RTSP_EINVAL; + +  /* ":" chars are not allowed in usernames for basic auth */ +  if (method == RTSP_AUTH_BASIC && g_strrstr (user, ":") != NULL) +    return RTSP_EINVAL; + +  g_free (conn->username); +  g_free (conn->passwd); + +  conn->auth_method = method; +  conn->username = g_strdup (user); +  conn->passwd = g_strdup (pass); + +  return RTSP_OK; +} diff --git a/gst/rtsp/rtspconnection.h b/gst/rtsp/rtspconnection.h index 36532fd0..2076950b 100644 --- a/gst/rtsp/rtspconnection.h +++ b/gst/rtsp/rtspconnection.h @@ -64,6 +64,11 @@ typedef struct _RTSPConnection    RTSPState     state;    gint          cseq;                   /* sequence number */    gchar         session_id[512];        /* session id */ + +  /* Authentication */ +  RTSPAuthMethod auth_method; +  gchar *username; +  gchar *passwd;  } RTSPConnection;  /* opening/closing a connection */ @@ -78,6 +83,10 @@ RTSPResult      rtsp_connection_receive (RTSPConnection *conn, RTSPMessage *mess  RTSPResult      rtsp_connection_flush   (RTSPConnection *conn, gboolean flush); +/* Configure Authentication data */ +RTSPResult      rtsp_connection_set_auth (RTSPConnection *conn,  +                    RTSPAuthMethod method, gchar *user, gchar *pass); +  G_END_DECLS  #endif /* __RTSP_CONNECTION_H__ */ diff --git a/gst/rtsp/rtspdefs.h b/gst/rtsp/rtspdefs.h index 43436188..f72a9a65 100644 --- a/gst/rtsp/rtspdefs.h +++ b/gst/rtsp/rtspdefs.h @@ -99,6 +99,16 @@ typedef enum {    RTSP_TEARDOWN         = (1 << 10),  } RTSPMethod; +/* Authentication methods, ordered by strength */ +typedef enum { +  RTSP_AUTH_NONE    = 0x00, +  RTSP_AUTH_BASIC   = 0x01, +  RTSP_AUTH_DIGEST  = 0x02 +} RTSPAuthMethod; + +/* Strongest available authentication method */ +#define RTSP_AUTH_MAX RTSP_AUTH_DIGEST +  typedef enum {    RTSP_HDR_INVALID, diff --git a/gst/rtsp/rtspurl.c b/gst/rtsp/rtspurl.c index 6ef0220e..cc2047c3 100644 --- a/gst/rtsp/rtspurl.c +++ b/gst/rtsp/rtspurl.c @@ -177,3 +177,20 @@ rtsp_url_get_port (RTSPUrl * url, guint16 * port)    return RTSP_OK;  } + +gchar * +rtsp_url_get_request_uri (RTSPUrl * url) +{ +  gchar *uri; + +  g_return_val_if_fail (url != NULL, NULL); + +  if (url->port != 0) { +    uri = g_strdup_printf ("rtsp://%s:%u/%s", url->host, url->port, +        url->abspath); +  } else { +    uri = g_strdup_printf ("rtsp://%s/%s", url->host, url->abspath); +  } + +  return uri; +} diff --git a/gst/rtsp/rtspurl.h b/gst/rtsp/rtspurl.h index 3c39e924..ce144f46 100644 --- a/gst/rtsp/rtspurl.h +++ b/gst/rtsp/rtspurl.h @@ -64,6 +64,7 @@ typedef struct _RTSPUrl {  RTSPResult      rtsp_url_parse          (const gchar *urlstr, RTSPUrl **url);  void            rtsp_url_free           (RTSPUrl *url); +gchar           *rtsp_url_get_request_uri (RTSPUrl *url);  RTSPResult      rtsp_url_set_port       (RTSPUrl *url, guint16 port);  RTSPResult      rtsp_url_get_port       (RTSPUrl *url, guint16 *port);  | 
