mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-18 00:35:27 +00:00
This introduces a "nucleitcp" protocol that Nuclei will use when making MySQL connections as part of its templates. Previously, this would register (and de-register!) a custom "tcp" dialer, and that applied globally, so any piece of software that used a MySQL database and included nuclei in SDK mode would have its database connections ripped out from under it due to the dialer hijacking. By using "nucleitcp" as the protocol, we are free to do whatever we want with the dialer and not impact any other packages. Within our `BuildDSN` function, we quietly replace the protocol to "nucleitcp" if it was "tcp", so nuclei developers don't have to do anything special to use this functionality; it will always do it.
90 lines
2.6 KiB
Go
90 lines
2.6 KiB
Go
package mysql
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
type (
|
|
// MySQLOptions defines the data source name (DSN) options required to connect to a MySQL database.
|
|
// along with other options like Timeout etc
|
|
// @example
|
|
// ```javascript
|
|
// const mysql = require('nuclei/mysql');
|
|
// const options = new mysql.MySQLOptions();
|
|
// options.Host = 'acme.com';
|
|
// options.Port = 3306;
|
|
// ```
|
|
MySQLOptions struct {
|
|
Host string // Host is the host name or IP address of the MySQL server.
|
|
Port int // Port is the port number on which the MySQL server is listening.
|
|
Protocol string // Protocol is the protocol used to connect to the MySQL server (ex: "tcp").
|
|
Username string // Username is the user name used to authenticate with the MySQL server.
|
|
Password string // Password is the password used to authenticate with the MySQL server.
|
|
DbName string // DbName is the name of the database to connect to on the MySQL server.
|
|
RawQuery string // QueryStr is the query string to append to the DSN (ex: "?tls=skip-verify").
|
|
Timeout int // Timeout is the timeout in seconds for the connection to the MySQL server.
|
|
}
|
|
)
|
|
|
|
// BuildDSN builds a MySQL data source name (DSN) from the given options.
|
|
// @example
|
|
// ```javascript
|
|
// const mysql = require('nuclei/mysql');
|
|
// const options = new mysql.MySQLOptions();
|
|
// options.Host = 'acme.com';
|
|
// options.Port = 3306;
|
|
// const dsn = mysql.BuildDSN(options);
|
|
// ```
|
|
func BuildDSN(opts MySQLOptions) (string, error) {
|
|
if opts.Host == "" || opts.Port <= 0 {
|
|
return "", fmt.Errorf("invalid host or port")
|
|
}
|
|
if opts.Protocol == "" {
|
|
opts.Protocol = "tcp"
|
|
}
|
|
// We're going to use a custom dialer when creating MySQL connections, so if we've been
|
|
// given "tcp" as the protocol, then quietly switch it to "nucleitcp", which we have
|
|
// already registered.
|
|
if opts.Protocol == "tcp" {
|
|
opts.Protocol = "nucleitcp"
|
|
}
|
|
if opts.DbName == "" {
|
|
opts.DbName = "/"
|
|
} else {
|
|
opts.DbName = "/" + opts.DbName
|
|
}
|
|
target := net.JoinHostPort(opts.Host, fmt.Sprintf("%d", opts.Port))
|
|
var dsn strings.Builder
|
|
dsn.WriteString(fmt.Sprintf("%v:%v", url.QueryEscape(opts.Username), opts.Password))
|
|
dsn.WriteString("@")
|
|
dsn.WriteString(fmt.Sprintf("%v(%v)", opts.Protocol, target))
|
|
if opts.DbName != "" {
|
|
dsn.WriteString(opts.DbName)
|
|
}
|
|
if opts.RawQuery != "" {
|
|
dsn.WriteString(opts.RawQuery)
|
|
}
|
|
return dsn.String(), nil
|
|
}
|
|
|
|
// @memo
|
|
func connectWithDSN(dsn string) (bool, error) {
|
|
db, err := sql.Open("mysql", dsn)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer db.Close()
|
|
db.SetMaxOpenConns(1)
|
|
db.SetMaxIdleConns(0)
|
|
|
|
_, err = db.Exec("select 1")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|