Example #1
0
static VALUE rb_mysql_get_ssl_cipher(VALUE self)
{
  const char *cipher;
  VALUE rb_str;
  GET_CLIENT(self);

  cipher = mysql_get_ssl_cipher(wrapper->client);

  if (cipher == NULL) {
    return Qnil;
  }

  rb_str = rb_str_new2(cipher);
#ifdef HAVE_RUBY_ENCODING_H
  rb_enc_associate(rb_str, rb_utf8_encoding());
#endif

  return rb_str;
}
Example #2
0
File: do_mysql.c Project: NZX/do
static void full_connect(VALUE self, MYSQL* db) {
  // Check to see if we're on the db machine.  If so, try to use the socket
  VALUE r_host, r_user, r_password, r_path, r_query, r_port;

  const char *host = "localhost", *user = "******"; 
  char *database = NULL, *socket = NULL, *password = NULL, *path = NULL;
  VALUE encoding = Qnil;

  MYSQL *result;

  int port = 3306;
  unsigned long client_flags = 0;
  int encoding_error;

  if((r_host = rb_iv_get(self, "@host")) != Qnil) {
    host     = StringValuePtr(r_host);
  }

  if((r_user = rb_iv_get(self, "@user")) != Qnil) {
    user     = StringValuePtr(r_user);
  }

  if((r_password = rb_iv_get(self, "@password")) != Qnil) {
    password = StringValuePtr(r_password);
  }

  if((r_port = rb_iv_get(self, "@port")) != Qnil) {
    port = NUM2INT(r_port);
  }

  if((r_path = rb_iv_get(self, "@path")) != Qnil) {
    path = StringValuePtr(r_path);
    database = strtok(path, "/");
  }

  if (NULL == database || 0 == strlen(database)) {
    rb_raise(eConnectionError, "Database must be specified");
  }

  r_query        = rb_iv_get(self, "@query");

  if (0 == strcasecmp(host, "localhost")) {
    socket = get_uri_option(r_query, "socket");
    if (NULL != socket) {
      rb_iv_set(self, "@using_socket", Qtrue);
    }
  }

#ifdef HAVE_MYSQL_SSL_SET
  char *ssl_client_key, *ssl_client_cert, *ssl_ca_cert, *ssl_ca_path, *ssl_cipher;
  VALUE r_ssl;

  if(rb_obj_is_kind_of(r_query, rb_cHash)) {
    r_ssl = rb_hash_aref(r_query, rb_str_new2("ssl"));

    if(rb_obj_is_kind_of(r_ssl, rb_cHash)) {
      ssl_client_key  = get_uri_option(r_ssl, "client_key");
      ssl_client_cert = get_uri_option(r_ssl, "client_cert");
      ssl_ca_cert     = get_uri_option(r_ssl, "ca_cert");
      ssl_ca_path     = get_uri_option(r_ssl, "ca_path");
      ssl_cipher      = get_uri_option(r_ssl, "cipher");

      assert_file_exists(ssl_client_key,  "client_key doesn't exist");
      assert_file_exists(ssl_client_cert, "client_cert doesn't exist");
      assert_file_exists(ssl_ca_cert,     "ca_cert doesn't exist");

      mysql_ssl_set(db, ssl_client_key, ssl_client_cert, ssl_ca_cert, ssl_ca_path, ssl_cipher);
    } else if(r_ssl != Qnil) {
      rb_raise(rb_eArgError, "ssl must be passed a hash");
    }
  }
#endif

  result = (MYSQL *)mysql_real_connect(
    db,
    host,
    user,
    password,
    database,
    port,
    socket,
    client_flags
  );

  if (NULL == result) {
    raise_error(self, db, Qnil);
  }

#ifdef HAVE_MYSQL_SSL_SET
  const char *ssl_cipher_used = mysql_get_ssl_cipher(db);

  if (NULL != ssl_cipher_used) {
    rb_iv_set(self, "@ssl_cipher", rb_str_new2(ssl_cipher_used));
  }
#endif

#ifdef MYSQL_OPT_RECONNECT
  my_bool reconnect = 1;
  mysql_options(db, MYSQL_OPT_RECONNECT, &reconnect);
#endif

  // Set the connections character set
  encoding = rb_iv_get(self, "@encoding");

  VALUE my_encoding = rb_hash_aref(CONST_GET(mEncoding, "MAP"), encoding);
  if(my_encoding != Qnil) {
    encoding_error = mysql_set_character_set(db, rb_str_ptr_readonly(my_encoding));
    if (0 != encoding_error) {
      raise_error(self, db, Qnil);
    } else {
#ifdef HAVE_RUBY_ENCODING_H
      rb_iv_set(self, "@encoding_id", INT2FIX(rb_enc_find_index(rb_str_ptr_readonly(encoding))));
#endif
      rb_iv_set(self, "@my_encoding", my_encoding);
    }
  } else {
    rb_warn("Encoding %s is not a known Ruby encoding for MySQL\n", rb_str_ptr_readonly(encoding));
    rb_iv_set(self, "@encoding", rb_str_new2("UTF-8"));
#ifdef HAVE_RUBY_ENCODING_H
    rb_iv_set(self, "@encoding_id", INT2FIX(rb_enc_find_index("UTF-8")));
#endif
    rb_iv_set(self, "@my_encoding", rb_str_new2("utf8"));
  }

  // Disable sql_auto_is_null
  cCommand_execute(Qnil, self, db, rb_str_new2("SET sql_auto_is_null = 0"));
  // removed NO_AUTO_VALUE_ON_ZERO because of MySQL bug http://bugs.mysql.com/bug.php?id=42270
  // added NO_BACKSLASH_ESCAPES so that backslashes should not be escaped as in other databases
   
  //4.x versions do not support certain session parameters  
  if(mysql_get_server_version(db) < 50000 ){
    cCommand_execute(Qnil, self, db, rb_str_new2("SET SESSION sql_mode = 'ANSI,NO_DIR_IN_CREATE,NO_UNSIGNED_SUBTRACTION'"));
  }else{
    cCommand_execute(Qnil, self, db, rb_str_new2("SET SESSION sql_mode = 'ANSI,NO_BACKSLASH_ESCAPES,NO_DIR_IN_CREATE,NO_ENGINE_SUBSTITUTION,NO_UNSIGNED_SUBTRACTION,TRADITIONAL'"));
  }
 
  rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
}
Example #3
0
void do_mysql_full_connect(VALUE self, MYSQL *db) {
  VALUE r_host = rb_iv_get(self, "@host");
  const char *host = "localhost";

  if (r_host != Qnil) {
    host = StringValuePtr(r_host);
  }

  VALUE r_user = rb_iv_get(self, "@user");
  const char *user = "******";

  if (r_user != Qnil) {
    user = StringValuePtr(r_user);
  }

  VALUE r_password = rb_iv_get(self, "@password");
  char *password = NULL;

  if (r_password != Qnil) {
    password = StringValuePtr(r_password);
  }

  VALUE r_port = rb_iv_get(self, "@port");
  int port = 3306;

  if (r_port != Qnil) {
    port = NUM2INT(r_port);
  }

  VALUE r_path = rb_iv_get(self, "@path");
  char *path = NULL;
  char *database = NULL;

  if (r_path != Qnil) {
    path = StringValuePtr(r_path);
    database = strtok(path, "/"); // not threadsafe
  }

  if (!database || !*database) {
    rb_raise(eConnectionError, "Database must be specified");
  }

  VALUE r_query = rb_iv_get(self, "@query");
  char *socket = NULL;

  // Check to see if we're on the db machine.  If so, try to use the socket
  if (strcasecmp(host, "localhost") == 0) {
    socket = data_objects_get_uri_option(r_query, "socket");

    if (socket) {
      rb_iv_set(self, "@using_socket", Qtrue);
    }
  }

#ifdef HAVE_MYSQL_SSL_SET
  char *ssl_client_key, *ssl_client_cert, *ssl_ca_cert, *ssl_ca_path, *ssl_cipher;
  VALUE r_ssl;

  if (rb_obj_is_kind_of(r_query, rb_cHash)) {
    r_ssl = rb_hash_aref(r_query, rb_str_new2("ssl"));

    if (rb_obj_is_kind_of(r_ssl, rb_cHash)) {
      ssl_client_key  = data_objects_get_uri_option(r_ssl, "client_key");
      ssl_client_cert = data_objects_get_uri_option(r_ssl, "client_cert");
      ssl_ca_cert     = data_objects_get_uri_option(r_ssl, "ca_cert");
      ssl_ca_path     = data_objects_get_uri_option(r_ssl, "ca_path");
      ssl_cipher      = data_objects_get_uri_option(r_ssl, "cipher");

      data_objects_assert_file_exists(ssl_client_key,  "client_key doesn't exist");
      data_objects_assert_file_exists(ssl_client_cert, "client_cert doesn't exist");
      data_objects_assert_file_exists(ssl_ca_cert,     "ca_cert doesn't exist");

      mysql_ssl_set(db, ssl_client_key, ssl_client_cert, ssl_ca_cert, ssl_ca_path, ssl_cipher);
    }
    else if (r_ssl != Qnil) {
      rb_raise(rb_eArgError, "ssl must be passed a hash");
    }
  }
#endif

  unsigned long client_flags = 0;

  MYSQL *result = mysql_real_connect(
    db,
    host,
    user,
    password,
    database,
    port,
    socket,
    client_flags
  );

  if (!result) {
    do_mysql_raise_error(self, db, Qnil);
  }

#ifdef HAVE_MYSQL_GET_SSL_CIPHER
  const char *ssl_cipher_used = mysql_get_ssl_cipher(db);

  if (ssl_cipher_used) {
    rb_iv_set(self, "@ssl_cipher", rb_str_new2(ssl_cipher_used));
  }
#endif

#ifdef MYSQL_OPT_RECONNECT
  my_bool reconnect = 1;
  mysql_options(db, MYSQL_OPT_RECONNECT, &reconnect);
#endif

  // We only support encoding for MySQL versions providing mysql_set_character_set.
  // Without this function there are potential issues with mysql_real_escape_string
  // since that doesn't take the character set into consideration when setting it
  // using a SET CHARACTER SET query. Since we don't want to stimulate these possible
  // issues we simply ignore it and assume the user has configured this correctly.

#ifdef HAVE_MYSQL_SET_CHARACTER_SET
  // Set the connections character set
  VALUE encoding = rb_iv_get(self, "@encoding");
  VALUE my_encoding = rb_hash_aref(data_objects_const_get(mEncoding, "MAP"), encoding);

  if (my_encoding != Qnil) {
    int encoding_error = mysql_set_character_set(db, rb_str_ptr_readonly(my_encoding));

    if (encoding_error != 0) {
      do_mysql_raise_error(self, db, Qnil);
    }
    else {
#ifdef HAVE_RUBY_ENCODING_H
      rb_iv_set(self, "@encoding_id", INT2FIX(rb_enc_find_index(rb_str_ptr_readonly(encoding))));
#endif

      rb_iv_set(self, "@my_encoding", my_encoding);
    }
  }
  else {
    rb_warn("Encoding %s is not a known Ruby encoding for MySQL\n", rb_str_ptr_readonly(encoding));
    rb_iv_set(self, "@encoding", rb_str_new2("UTF-8"));
#ifdef HAVE_RUBY_ENCODING_H
    rb_iv_set(self, "@encoding_id", INT2FIX(rb_enc_find_index("UTF-8")));
#endif
    rb_iv_set(self, "@my_encoding", rb_str_new2("utf8"));
  }
#endif

  // Disable sql_auto_is_null
  do_mysql_cCommand_execute(Qnil, self, db, rb_str_new2("SET sql_auto_is_null = 0"));
  // removed NO_AUTO_VALUE_ON_ZERO because of MySQL bug http://bugs.mysql.com/bug.php?id=42270
  // added NO_BACKSLASH_ESCAPES so that backslashes should not be escaped as in other databases

// For really anscient MySQL versions we don't attempt any strictness
#ifdef HAVE_MYSQL_GET_SERVER_VERSION
  //4.x versions do not support certain session parameters
  if (mysql_get_server_version(db) < 50000) {
    do_mysql_cCommand_execute(Qnil, self, db, rb_str_new2("SET SESSION sql_mode = 'ANSI,NO_DIR_IN_CREATE,NO_UNSIGNED_SUBTRACTION'"));
  }
  else {
    do_mysql_cCommand_execute(Qnil, self, db, rb_str_new2("SET SESSION sql_mode = 'ANSI,NO_BACKSLASH_ESCAPES,NO_DIR_IN_CREATE,NO_ENGINE_SUBSTITUTION,NO_UNSIGNED_SUBTRACTION,TRADITIONAL'"));
  }
#endif

  rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
}
Example #4
0
static int mysql_drive_session(eventer_t e, int mask, void *closure,
                                  struct timeval *now) {
  const char *dsn, *sql;
  char sql_buff[8192];
  char dsn_buff[512];
  mysql_check_info_t *ci = closure;
  noit_check_t *check = ci->check;
  struct timeval t1, t2, diff;
  mtev_hash_table dsn_h = MTEV_HASH_EMPTY;
  const char *host=NULL;
  const char *user=NULL;
  const char *password=NULL;
  const char *dbname=NULL;
  const char *port_s=NULL;
  const char *socket=NULL;
  const char *sslmode=NULL;
  u_int32_t port;
  unsigned long client_flag = CLIENT_IGNORE_SIGPIPE;
  unsigned int timeout;

  if(mask & (EVENTER_READ | EVENTER_WRITE)) {
    /* this case is impossible from the eventer.  It is called as
     * such on the synchronous completion of the event.
     */
    mysql_log_results(ci->self, ci->check);
    mysql_cleanup(ci->self, ci->check);
    check->flags &= ~NP_RUNNING;
    return 0;
  }
  switch(mask) {
    case EVENTER_ASYNCH_WORK:
      ci->connect_duration = NULL;
      ci->query_duration = NULL;

      FETCH_CONFIG_OR(dsn, "");
      noit_check_interpolate(dsn_buff, sizeof(dsn_buff), dsn,
                             &ci->attrs, check->config);

      mysql_parse_dsn(dsn_buff, &dsn_h);
      mtev_hash_retrieve(&dsn_h, "host", strlen("host"), (void**)&host);
      mtev_hash_retrieve(&dsn_h, "user", strlen("user"), (void**)&user);
      mtev_hash_retrieve(&dsn_h, "password", strlen("password"), (void**)&password);
      mtev_hash_retrieve(&dsn_h, "dbname", strlen("dbname"), (void**)&dbname);
      mtev_hash_retrieve(&dsn_h, "port", strlen("port"), (void**)&port_s);
      if(mtev_hash_retrieve(&dsn_h, "sslmode", strlen("sslmode"), (void**)&sslmode) &&
         !strcmp(sslmode, "require"))
        client_flag |= CLIENT_SSL;
      port = port_s ? strtol(port_s, NULL, 10) : 3306;
      mtev_hash_retrieve(&dsn_h, "socket", strlen("socket"), (void**)&socket);

      ci->conn = mysql_init(NULL); /* allocate us a handle */
      if(!ci->conn) AVAIL_BAIL("mysql_init failed");
      timeout = check->timeout / 1000;
      mysql_options(ci->conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&timeout);
      if(!mysql_real_connect(ci->conn, host, user, password,
                             dbname, port, socket, client_flag)) {
        mtevL(noit_stderr, "error during mysql_real_connect: %s\n",
          mysql_error(ci->conn));
        AVAIL_BAIL(mysql_error(ci->conn));
      }
      if(mysql_ping(ci->conn))
        AVAIL_BAIL(mysql_error(ci->conn));

#if MYSQL_VERSION_ID >= 50000
      if (sslmode && !strcmp(sslmode, "require")) {
        /* mysql has a bad habit of silently failing to establish ssl and
         * falling back to unencrypted, so after making the connection, let's 
         * check that we're actually using SSL by checking for a non-NULL 
         * return value from mysql_get_ssl_cipher().
         */
        if (mysql_get_ssl_cipher(ci->conn) == NULL) {
          mtevL(nldeb, "mysql_get_ssl_cipher() returns NULL, but SSL mode required.");
          AVAIL_BAIL("mysql_get_ssl_cipher() returns NULL, but SSL mode required.");
        }
      }
#endif

      gettimeofday(&t1, NULL);
      sub_timeval(t1, check->last_fire_time, &diff);
      ci->connect_duration_d = diff.tv_sec * 1000.0 + diff.tv_usec / 1000.0;
      ci->connect_duration = &ci->connect_duration_d;

      FETCH_CONFIG_OR(sql, "");
      noit_check_interpolate(sql_buff, sizeof(sql_buff), sql,
                             &ci->attrs, check->config);
      if (mysql_query(ci->conn, sql_buff))
        AVAIL_BAIL(mysql_error(ci->conn));

      gettimeofday(&t2, NULL);
      sub_timeval(t2, t1, &diff);
      ci->query_duration_d = diff.tv_sec * 1000.0 + diff.tv_usec / 1000.0;
      ci->query_duration = &ci->query_duration_d;

      ci->result = mysql_store_result(ci->conn);
      if(!ci->result) AVAIL_BAIL("mysql_store_result failed");
      ci->rv = mysql_num_rows(ci->result);
      mysql_ingest_stats(ci);
      if(ci->result) {
        MYSQL_RES *result_swap = ci->result;
        ci->result = NULL;
        mysql_free_result(result_swap);
      }
      if(ci->conn) {
        MYSQL *conn_swap = ci->conn;
        ci->conn = NULL;
        mysql_close(conn_swap);
      }
      ci->timed_out = 0;
      mtev_hash_destroy(&dsn_h, free, free);
      return 0;
      break;
    case EVENTER_ASYNCH_CLEANUP:
      /* This sets us up for a completion call. */
      e->mask = EVENTER_READ | EVENTER_WRITE;
      break;
    default:
      abort();
  }
  return 0;
}