github.com/vmware/govmomi@v0.37.2/simulator/simulator.go (about)

     1  /*
     2  Copyright (c) 2017-2023 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package simulator
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"crypto/tls"
    23  	"crypto/x509"
    24  	"encoding/base64"
    25  	"encoding/json"
    26  	"encoding/pem"
    27  	"fmt"
    28  	"io"
    29  	"log"
    30  	"net"
    31  	"net/http"
    32  	"net/url"
    33  	"os"
    34  	"path"
    35  	"reflect"
    36  	"sort"
    37  	"strconv"
    38  	"strings"
    39  
    40  	"github.com/google/uuid"
    41  
    42  	"github.com/vmware/govmomi/find"
    43  	"github.com/vmware/govmomi/object"
    44  	"github.com/vmware/govmomi/simulator/internal"
    45  	"github.com/vmware/govmomi/vim25"
    46  	"github.com/vmware/govmomi/vim25/mo"
    47  	"github.com/vmware/govmomi/vim25/soap"
    48  	"github.com/vmware/govmomi/vim25/types"
    49  	"github.com/vmware/govmomi/vim25/xml"
    50  )
    51  
    52  var (
    53  	// Trace when set to true, writes SOAP traffic to stderr
    54  	Trace = false
    55  
    56  	// TraceFile is the output file when Trace = true
    57  	TraceFile = os.Stderr
    58  
    59  	// DefaultLogin for authentication
    60  	DefaultLogin = url.UserPassword("user", "pass")
    61  )
    62  
    63  // Method encapsulates a decoded SOAP client request
    64  type Method struct {
    65  	Name   string
    66  	This   types.ManagedObjectReference
    67  	Header soap.Header
    68  	Body   types.AnyType
    69  }
    70  
    71  // Service decodes incoming requests and dispatches to a Handler
    72  type Service struct {
    73  	client *vim25.Client
    74  	sm     *SessionManager
    75  	sdk    map[string]*Registry
    76  	funcs  []handleFunc
    77  	delay  *DelayConfig
    78  
    79  	readAll func(io.Reader) ([]byte, error)
    80  
    81  	Listen   *url.URL
    82  	TLS      *tls.Config
    83  	ServeMux *http.ServeMux
    84  	// RegisterEndpoints will initialize any endpoints added via RegisterEndpoint
    85  	RegisterEndpoints bool
    86  }
    87  
    88  // Server provides a simulator Service over HTTP
    89  type Server struct {
    90  	*internal.Server
    91  	URL    *url.URL
    92  	Tunnel int
    93  
    94  	caFile string
    95  }
    96  
    97  // New returns an initialized simulator Service instance
    98  func New(instance *ServiceInstance) *Service {
    99  	s := &Service{
   100  		readAll: io.ReadAll,
   101  		sm:      Map.SessionManager(),
   102  		sdk:     make(map[string]*Registry),
   103  	}
   104  
   105  	s.client, _ = vim25.NewClient(context.Background(), s)
   106  
   107  	return s
   108  }
   109  
   110  type serverFaultBody struct {
   111  	Reason *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
   112  }
   113  
   114  func (b *serverFaultBody) Fault() *soap.Fault { return b.Reason }
   115  
   116  func serverFault(msg string) soap.HasFault {
   117  	return &serverFaultBody{Reason: Fault(msg, &types.InvalidRequest{})}
   118  }
   119  
   120  // Fault wraps the given message and fault in a soap.Fault
   121  func Fault(msg string, fault types.BaseMethodFault) *soap.Fault {
   122  	f := &soap.Fault{
   123  		Code:   "ServerFaultCode",
   124  		String: msg,
   125  	}
   126  
   127  	f.Detail.Fault = fault
   128  
   129  	return f
   130  }
   131  
   132  func tracef(format string, v ...interface{}) {
   133  	if Trace {
   134  		log.Printf(format, v...)
   135  	}
   136  }
   137  
   138  func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
   139  	handler := ctx.Map.Get(method.This)
   140  	session := ctx.Session
   141  	ctx.Caller = &method.This
   142  
   143  	if ctx.Map.Handler != nil {
   144  		h, fault := ctx.Map.Handler(ctx, method)
   145  		if fault != nil {
   146  			return &serverFaultBody{Reason: Fault("", fault)}
   147  		}
   148  		if h != nil {
   149  			handler = h
   150  		}
   151  	}
   152  
   153  	if session == nil {
   154  		switch method.Name {
   155  		case "RetrieveServiceContent", "PbmRetrieveServiceContent", "Fetch", "List", "Login", "LoginByToken", "LoginExtensionByCertificate", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
   156  			// ok for now, TODO: authz
   157  		default:
   158  			fault := &types.NotAuthenticated{
   159  				NoPermission: types.NoPermission{
   160  					Object:      &method.This,
   161  					PrivilegeId: "System.View",
   162  				},
   163  			}
   164  			return &serverFaultBody{Reason: Fault("", fault)}
   165  		}
   166  	} else {
   167  		// Prefer the Session.Registry, ServiceContent.PropertyCollector filter field for example is per-session
   168  		if h := session.Get(method.This); h != nil {
   169  			handler = h
   170  		}
   171  	}
   172  
   173  	if handler == nil {
   174  		msg := fmt.Sprintf("managed object not found: %s", method.This)
   175  		log.Print(msg)
   176  		fault := &types.ManagedObjectNotFound{Obj: method.This}
   177  		return &serverFaultBody{Reason: Fault(msg, fault)}
   178  	}
   179  
   180  	// Lowercase methods can't be accessed outside their package
   181  	name := strings.Title(method.Name)
   182  
   183  	if strings.HasSuffix(name, vTaskSuffix) {
   184  		// Make golint happy renaming "Foo_Task" -> "FooTask"
   185  		name = name[:len(name)-len(vTaskSuffix)] + sTaskSuffix
   186  	}
   187  
   188  	m := reflect.ValueOf(handler).MethodByName(name)
   189  	if !m.IsValid() {
   190  		msg := fmt.Sprintf("%s does not implement: %s", method.This, method.Name)
   191  		log.Print(msg)
   192  		fault := &types.MethodNotFound{Receiver: method.This, Method: method.Name}
   193  		return &serverFaultBody{Reason: Fault(msg, fault)}
   194  	}
   195  
   196  	if e, ok := handler.(mo.Entity); ok {
   197  		for _, dm := range e.Entity().DisabledMethod {
   198  			if name == dm {
   199  				msg := fmt.Sprintf("%s method is disabled: %s", method.This, method.Name)
   200  				fault := &types.MethodDisabled{}
   201  				return &serverFaultBody{Reason: Fault(msg, fault)}
   202  			}
   203  		}
   204  	}
   205  
   206  	// We have a valid call. Introduce a delay if requested
   207  	if s.delay != nil {
   208  		s.delay.delay(method.Name)
   209  	}
   210  
   211  	var args, res []reflect.Value
   212  	if m.Type().NumIn() == 2 {
   213  		args = append(args, reflect.ValueOf(ctx))
   214  	}
   215  	args = append(args, reflect.ValueOf(method.Body))
   216  	ctx.Map.WithLock(ctx, handler, func() {
   217  		res = m.Call(args)
   218  	})
   219  
   220  	return res[0].Interface().(soap.HasFault)
   221  }
   222  
   223  // internalSession is the session for use by the in-memory client (Service.RoundTrip)
   224  var internalSession = &Session{
   225  	UserSession: types.UserSession{
   226  		Key: uuid.New().String(),
   227  	},
   228  	Registry: NewRegistry(),
   229  }
   230  
   231  // RoundTrip implements the soap.RoundTripper interface in process.
   232  // Rather than encode/decode SOAP over HTTP, this implementation uses reflection.
   233  func (s *Service) RoundTrip(ctx context.Context, request, response soap.HasFault) error {
   234  	field := func(r soap.HasFault, name string) reflect.Value {
   235  		return reflect.ValueOf(r).Elem().FieldByName(name)
   236  	}
   237  
   238  	// Every struct passed to soap.RoundTrip has "Req" and "Res" fields
   239  	req := field(request, "Req")
   240  
   241  	// Every request has a "This" field.
   242  	this := req.Elem().FieldByName("This")
   243  	// Copy request body
   244  	body := reflect.New(req.Type().Elem())
   245  	deepCopy(req.Interface(), body.Interface())
   246  
   247  	method := &Method{
   248  		Name: req.Elem().Type().Name(),
   249  		This: this.Interface().(types.ManagedObjectReference),
   250  		Body: body.Interface(),
   251  	}
   252  
   253  	res := s.call(&Context{
   254  		Map:     Map,
   255  		Context: ctx,
   256  		Session: internalSession,
   257  	}, method)
   258  
   259  	if err := res.Fault(); err != nil {
   260  		return soap.WrapSoapFault(err)
   261  	}
   262  
   263  	field(response, "Res").Set(field(res, "Res"))
   264  
   265  	return nil
   266  }
   267  
   268  // soapEnvelope is a copy of soap.Envelope, with namespace changed to "soapenv",
   269  // and additional namespace attributes required by some client libraries.
   270  // Go still has issues decoding with such a namespace, but encoding is ok.
   271  type soapEnvelope struct {
   272  	XMLName xml.Name    `xml:"soapenv:Envelope"`
   273  	Enc     string      `xml:"xmlns:soapenc,attr"`
   274  	Env     string      `xml:"xmlns:soapenv,attr"`
   275  	XSD     string      `xml:"xmlns:xsd,attr"`
   276  	XSI     string      `xml:"xmlns:xsi,attr"`
   277  	Body    interface{} `xml:"soapenv:Body"`
   278  }
   279  
   280  type faultDetail struct {
   281  	Fault types.AnyType
   282  }
   283  
   284  // soapFault is a copy of soap.Fault, with the same changes as soapEnvelope
   285  type soapFault struct {
   286  	XMLName xml.Name `xml:"soapenv:Fault"`
   287  	Code    string   `xml:"faultcode"`
   288  	String  string   `xml:"faultstring"`
   289  	Detail  struct {
   290  		Fault *faultDetail
   291  	} `xml:"detail"`
   292  }
   293  
   294  // MarshalXML renames the start element from "Fault" to "${Type}Fault"
   295  func (d *faultDetail) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
   296  	kind := reflect.TypeOf(d.Fault).Elem().Name()
   297  	start.Name.Local = kind + "Fault"
   298  	start.Attr = append(start.Attr,
   299  		xml.Attr{
   300  			Name:  xml.Name{Local: "xmlns"},
   301  			Value: "urn:" + vim25.Namespace,
   302  		},
   303  		xml.Attr{
   304  			Name:  xml.Name{Local: "xsi:type"},
   305  			Value: kind,
   306  		})
   307  	return e.EncodeElement(d.Fault, start)
   308  }
   309  
   310  // response sets xml.Name.Space when encoding Body.
   311  // Note that namespace is intentionally omitted in the vim25/methods/methods.go Body.Res field tags.
   312  type response struct {
   313  	Namespace string
   314  	Body      soap.HasFault
   315  }
   316  
   317  func (r *response) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
   318  	val := reflect.ValueOf(r.Body).Elem().FieldByName("Res")
   319  	if !val.IsValid() {
   320  		return fmt.Errorf("%T: invalid response type (missing 'Res' field)", r.Body)
   321  	}
   322  	if val.IsNil() {
   323  		return fmt.Errorf("%T: invalid response (nil 'Res' field)", r.Body)
   324  	}
   325  	res := xml.StartElement{
   326  		Name: xml.Name{
   327  			Space: "urn:" + r.Namespace,
   328  			Local: val.Elem().Type().Name(),
   329  		},
   330  	}
   331  	if err := e.EncodeToken(start); err != nil {
   332  		return err
   333  	}
   334  	if err := e.EncodeElement(val.Interface(), res); err != nil {
   335  		return err
   336  	}
   337  	return e.EncodeToken(start.End())
   338  }
   339  
   340  // About generates some info about the simulator.
   341  func (s *Service) About(w http.ResponseWriter, r *http.Request) {
   342  	var about struct {
   343  		Methods []string `json:"methods"`
   344  		Types   []string `json:"types"`
   345  	}
   346  
   347  	seen := make(map[string]bool)
   348  
   349  	f := reflect.TypeOf((*soap.HasFault)(nil)).Elem()
   350  
   351  	for _, sdk := range s.sdk {
   352  		for _, obj := range sdk.objects {
   353  			kind := obj.Reference().Type
   354  			if seen[kind] {
   355  				continue
   356  			}
   357  			seen[kind] = true
   358  
   359  			about.Types = append(about.Types, kind)
   360  
   361  			t := reflect.TypeOf(obj)
   362  			for i := 0; i < t.NumMethod(); i++ {
   363  				m := t.Method(i)
   364  				if seen[m.Name] {
   365  					continue
   366  				}
   367  				seen[m.Name] = true
   368  
   369  				in := m.Type.NumIn()
   370  				if in < 2 || in > 3 { // at least 2 params (receiver and request), optionally a 3rd param (context)
   371  					continue
   372  				}
   373  				if m.Type.NumOut() != 1 || m.Type.Out(0) != f { // all methods return soap.HasFault
   374  					continue
   375  				}
   376  
   377  				about.Methods = append(about.Methods, strings.Replace(m.Name, "Task", "_Task", 1))
   378  			}
   379  		}
   380  	}
   381  
   382  	sort.Strings(about.Methods)
   383  	sort.Strings(about.Types)
   384  
   385  	w.Header().Set("Content-Type", "application/json")
   386  	enc := json.NewEncoder(w)
   387  	enc.SetIndent("", "  ")
   388  	_ = enc.Encode(&about)
   389  }
   390  
   391  var endpoints []func(*Service, *Registry)
   392  
   393  // RegisterEndpoint funcs are called after the Server is initialized if Service.RegisterEndpoints=true.
   394  // Such a func would typically register a SOAP endpoint via Service.RegisterSDK or REST endpoint via Service.Handle
   395  func RegisterEndpoint(endpoint func(*Service, *Registry)) {
   396  	endpoints = append(endpoints, endpoint)
   397  }
   398  
   399  // Handle registers the handler for the given pattern with Service.ServeMux.
   400  func (s *Service) Handle(pattern string, handler http.Handler) {
   401  	s.ServeMux.Handle(pattern, handler)
   402  	// Not ideal, but avoids having to add yet another registration mechanism
   403  	// so we can optionally use vapi/simulator internally.
   404  	if m, ok := handler.(tagManager); ok {
   405  		s.sdk[vim25.Path].tagManager = m
   406  	}
   407  }
   408  
   409  type muxHandleFunc interface {
   410  	HandleFunc(string, func(http.ResponseWriter, *http.Request))
   411  }
   412  
   413  type handleFunc struct {
   414  	pattern string
   415  	handler func(http.ResponseWriter, *http.Request)
   416  }
   417  
   418  // HandleFunc dispatches to http.ServeMux.HandleFunc after all endpoints have been registered.
   419  // This allows dispatching to an endpoint's HandleFunc impl, such as vapi/simulator for example.
   420  func (s *Service) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
   421  	s.funcs = append(s.funcs, handleFunc{pattern, handler})
   422  }
   423  
   424  // RegisterSDK adds an HTTP handler for the Registry's Path and Namespace.
   425  // If r.Path is already registered, r's objects are added to the existing Registry.
   426  // An optional set of aliases can be provided to register the same handler for
   427  // multiple paths.
   428  func (s *Service) RegisterSDK(r *Registry, alias ...string) {
   429  	if existing, ok := s.sdk[r.Path]; ok {
   430  		for id, obj := range r.objects {
   431  			existing.objects[id] = obj
   432  		}
   433  		return
   434  	}
   435  
   436  	if s.ServeMux == nil {
   437  		s.ServeMux = http.NewServeMux()
   438  	}
   439  
   440  	s.sdk[r.Path] = r
   441  	s.ServeMux.HandleFunc(r.Path, s.ServeSDK)
   442  
   443  	for _, p := range alias {
   444  		s.sdk[p] = r
   445  		s.ServeMux.HandleFunc(p, s.ServeSDK)
   446  	}
   447  }
   448  
   449  // StatusSDK can be used to simulate an /sdk HTTP response code other than 200.
   450  // The value of StatusSDK is restored to http.StatusOK after 1 response.
   451  // This can be useful to test vim25.Retry() for example.
   452  var StatusSDK = http.StatusOK
   453  
   454  // ServeSDK implements the http.Handler interface
   455  func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
   456  	if r.Method != http.MethodPost {
   457  		w.WriteHeader(http.StatusMethodNotAllowed)
   458  		return
   459  	}
   460  
   461  	if StatusSDK != http.StatusOK {
   462  		w.WriteHeader(StatusSDK)
   463  		StatusSDK = http.StatusOK // reset
   464  		return
   465  	}
   466  
   467  	body, err := s.readAll(r.Body)
   468  	_ = r.Body.Close()
   469  	if err != nil {
   470  		log.Printf("error reading body: %s", err)
   471  		w.WriteHeader(http.StatusBadRequest)
   472  		return
   473  	}
   474  
   475  	if Trace {
   476  		fmt.Fprintf(TraceFile, "Request: %s\n", string(body))
   477  	}
   478  
   479  	ctx := &Context{
   480  		req: r,
   481  		res: w,
   482  		svc: s,
   483  
   484  		Map:     s.sdk[r.URL.Path],
   485  		Context: context.Background(),
   486  	}
   487  	ctx.Map.WithLock(ctx, s.sm, ctx.mapSession)
   488  
   489  	var res soap.HasFault
   490  	var soapBody interface{}
   491  
   492  	method, err := UnmarshalBody(ctx.Map.typeFunc, body)
   493  	if err != nil {
   494  		res = serverFault(err.Error())
   495  	} else {
   496  		ctx.Header = method.Header
   497  		if method.Name == "Fetch" {
   498  			// Redirect any Fetch method calls to the PropertyCollector singleton
   499  			method.This = ctx.Map.content().PropertyCollector
   500  		}
   501  		res = s.call(ctx, method)
   502  	}
   503  
   504  	if f := res.Fault(); f != nil {
   505  		w.WriteHeader(http.StatusInternalServerError)
   506  
   507  		// the generated method/*Body structs use the '*soap.Fault' type,
   508  		// so we need our own Body type to use the modified '*soapFault' type.
   509  		soapBody = struct {
   510  			Fault *soapFault
   511  		}{
   512  			&soapFault{
   513  				Code:   f.Code,
   514  				String: f.String,
   515  				Detail: struct {
   516  					Fault *faultDetail
   517  				}{&faultDetail{f.Detail.Fault}},
   518  			},
   519  		}
   520  	} else {
   521  		w.WriteHeader(http.StatusOK)
   522  
   523  		soapBody = &response{ctx.Map.Namespace, res}
   524  	}
   525  
   526  	var out bytes.Buffer
   527  
   528  	fmt.Fprint(&out, xml.Header)
   529  	e := xml.NewEncoder(&out)
   530  	err = e.Encode(&soapEnvelope{
   531  		Enc:  "http://schemas.xmlsoap.org/soap/encoding/",
   532  		Env:  "http://schemas.xmlsoap.org/soap/envelope/",
   533  		XSD:  "http://www.w3.org/2001/XMLSchema",
   534  		XSI:  "http://www.w3.org/2001/XMLSchema-instance",
   535  		Body: soapBody,
   536  	})
   537  	if err == nil {
   538  		err = e.Flush()
   539  	}
   540  
   541  	if err != nil {
   542  		log.Printf("error encoding %s response: %s", method.Name, err)
   543  		return
   544  	}
   545  
   546  	if Trace {
   547  		fmt.Fprintf(TraceFile, "Response: %s\n", out.String())
   548  	}
   549  
   550  	_, _ = w.Write(out.Bytes())
   551  }
   552  
   553  func (s *Service) findDatastore(query url.Values) (*Datastore, error) {
   554  	ctx := context.Background()
   555  
   556  	finder := find.NewFinder(s.client, false)
   557  	dc, err := finder.DatacenterOrDefault(ctx, query.Get("dcPath"))
   558  	if err != nil {
   559  		return nil, err
   560  	}
   561  
   562  	finder.SetDatacenter(dc)
   563  
   564  	ds, err := finder.DatastoreOrDefault(ctx, query.Get("dsName"))
   565  	if err != nil {
   566  		return nil, err
   567  	}
   568  
   569  	return Map.Get(ds.Reference()).(*Datastore), nil
   570  }
   571  
   572  const folderPrefix = "/folder/"
   573  
   574  // ServeDatastore handler for Datastore access via /folder path.
   575  func (s *Service) ServeDatastore(w http.ResponseWriter, r *http.Request) {
   576  	ds, ferr := s.findDatastore(r.URL.Query())
   577  	if ferr != nil {
   578  		log.Printf("failed to locate datastore with query params: %s", r.URL.RawQuery)
   579  		w.WriteHeader(http.StatusNotFound)
   580  		return
   581  	}
   582  
   583  	r.URL.Path = strings.TrimPrefix(r.URL.Path, folderPrefix)
   584  	p := path.Join(ds.Info.GetDatastoreInfo().Url, r.URL.Path)
   585  
   586  	switch r.Method {
   587  	case http.MethodPost:
   588  		_, err := os.Stat(p)
   589  		if err == nil {
   590  			// File exists
   591  			w.WriteHeader(http.StatusConflict)
   592  			return
   593  		}
   594  
   595  		// File does not exist, fallthrough to create via PUT logic
   596  		fallthrough
   597  	case http.MethodPut:
   598  		dir := path.Dir(p)
   599  		_ = os.MkdirAll(dir, 0700)
   600  
   601  		f, err := os.Create(p)
   602  		if err != nil {
   603  			log.Printf("failed to %s '%s': %s", r.Method, p, err)
   604  			w.WriteHeader(http.StatusInternalServerError)
   605  			return
   606  		}
   607  		defer f.Close()
   608  
   609  		_, _ = io.Copy(f, r.Body)
   610  	default:
   611  		fs := http.FileServer(http.Dir(ds.Info.GetDatastoreInfo().Url))
   612  
   613  		fs.ServeHTTP(w, r)
   614  	}
   615  }
   616  
   617  // ServiceVersions handler for the /sdk/vimServiceVersions.xml path.
   618  func (s *Service) ServiceVersions(w http.ResponseWriter, r *http.Request) {
   619  	const versions = xml.Header + `<namespaces version="1.0">
   620   <namespace>
   621    <name>urn:vim25</name>
   622    <version>%s</version>
   623    <priorVersions>
   624     <version>6.0</version>
   625     <version>5.5</version>
   626    </priorVersions>
   627   </namespace>
   628  </namespaces>
   629  `
   630  	fmt.Fprintf(w, versions, s.client.ServiceContent.About.ApiVersion)
   631  }
   632  
   633  // ServiceVersionsVsan handler for the /sdk/vsanServiceVersions.xml path.
   634  func (s *Service) ServiceVersionsVsan(w http.ResponseWriter, r *http.Request) {
   635  	const versions = xml.Header + `<namespaces version="1.0">
   636   <namespace>
   637    <name>urn:vsan</name>
   638    <version>%s</version>
   639    <priorVersions>
   640     <version>6.7</version>
   641     <version>6.6</version>
   642    </priorVersions>
   643   </namespace>
   644  </namespaces>
   645  `
   646  	fmt.Fprintf(w, versions, s.client.ServiceContent.About.ApiVersion)
   647  }
   648  
   649  // defaultIP returns addr.IP if specified, otherwise attempts to find a non-loopback ipv4 IP
   650  func defaultIP(addr *net.TCPAddr) string {
   651  	if !addr.IP.IsUnspecified() {
   652  		return addr.IP.String()
   653  	}
   654  
   655  	nics, err := net.Interfaces()
   656  	if err != nil {
   657  		return addr.IP.String()
   658  	}
   659  
   660  	for _, nic := range nics {
   661  		if nic.Name == "docker0" || strings.HasPrefix(nic.Name, "vmnet") {
   662  			continue
   663  		}
   664  		addrs, aerr := nic.Addrs()
   665  		if aerr != nil {
   666  			continue
   667  		}
   668  		for _, addr := range addrs {
   669  			if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() {
   670  				if ip.IP.To4() != nil {
   671  					return ip.IP.String()
   672  				}
   673  			}
   674  		}
   675  	}
   676  
   677  	return addr.IP.String()
   678  }
   679  
   680  // NewServer returns an http Server instance for the given service
   681  func (s *Service) NewServer() *Server {
   682  	s.RegisterSDK(Map, Map.Path+"/vimService")
   683  
   684  	mux := s.ServeMux
   685  	mux.HandleFunc(Map.Path+"/vimServiceVersions.xml", s.ServiceVersions)
   686  	mux.HandleFunc(Map.Path+"/vsanServiceVersions.xml", s.ServiceVersionsVsan)
   687  	mux.HandleFunc(folderPrefix, s.ServeDatastore)
   688  	mux.HandleFunc(guestPrefix, ServeGuest)
   689  	mux.HandleFunc(nfcPrefix, ServeNFC)
   690  	mux.HandleFunc("/about", s.About)
   691  
   692  	if s.Listen == nil {
   693  		s.Listen = new(url.URL)
   694  	}
   695  	ts := internal.NewUnstartedServer(mux, s.Listen.Host)
   696  	addr := ts.Listener.Addr().(*net.TCPAddr)
   697  	port := strconv.Itoa(addr.Port)
   698  	u := &url.URL{
   699  		Scheme: "http",
   700  		Host:   net.JoinHostPort(defaultIP(addr), port),
   701  		Path:   Map.Path,
   702  	}
   703  	if s.TLS != nil {
   704  		u.Scheme += "s"
   705  	}
   706  
   707  	// Redirect clients to this http server, rather than HostSystem.Name
   708  	Map.SessionManager().ServiceHostName = u.Host
   709  
   710  	// Add vcsim config to OptionManager for use by SDK handlers (see lookup/simulator for example)
   711  	m := Map.OptionManager()
   712  	for i := range m.Setting {
   713  		setting := m.Setting[i].GetOptionValue()
   714  
   715  		if strings.HasSuffix(setting.Key, ".uri") {
   716  			// Rewrite any URIs with vcsim's host:port
   717  			endpoint, err := url.Parse(setting.Value.(string))
   718  			if err == nil {
   719  				endpoint.Scheme = u.Scheme
   720  				endpoint.Host = u.Host
   721  				setting.Value = endpoint.String()
   722  			}
   723  		}
   724  	}
   725  	m.Setting = append(m.Setting,
   726  		&types.OptionValue{
   727  			Key:   "vcsim.server.url",
   728  			Value: u.String(),
   729  		},
   730  	)
   731  
   732  	u.User = s.Listen.User
   733  	if u.User == nil {
   734  		u.User = DefaultLogin
   735  	}
   736  	s.Listen = u
   737  
   738  	if s.RegisterEndpoints {
   739  		for i := range endpoints {
   740  			endpoints[i](s, Map)
   741  		}
   742  	}
   743  
   744  	for _, f := range s.funcs {
   745  		pattern := &url.URL{Path: f.pattern}
   746  		endpoint, _ := s.ServeMux.Handler(&http.Request{URL: pattern})
   747  
   748  		if mux, ok := endpoint.(muxHandleFunc); ok {
   749  			mux.HandleFunc(f.pattern, f.handler) // e.g. vapi/simulator
   750  		} else {
   751  			s.ServeMux.HandleFunc(f.pattern, f.handler)
   752  		}
   753  	}
   754  
   755  	if s.TLS != nil {
   756  		ts.TLS = s.TLS
   757  		ts.TLS.ClientAuth = tls.RequestClientCert // Used by SessionManager.LoginExtensionByCertificate
   758  		Map.SessionManager().TLSCert = func() string {
   759  			return base64.StdEncoding.EncodeToString(ts.TLS.Certificates[0].Certificate[0])
   760  		}
   761  		ts.StartTLS()
   762  	} else {
   763  		ts.Start()
   764  	}
   765  
   766  	return &Server{
   767  		Server: ts,
   768  		URL:    u,
   769  	}
   770  }
   771  
   772  // Certificate returns the TLS certificate for the Server if started with TLS enabled.
   773  // This method will panic if TLS is not enabled for the server.
   774  func (s *Server) Certificate() *x509.Certificate {
   775  	// By default httptest.StartTLS uses http/internal.LocalhostCert, which we can access here:
   776  	cert, _ := x509.ParseCertificate(s.TLS.Certificates[0].Certificate[0])
   777  	return cert
   778  }
   779  
   780  // CertificateInfo returns Server.Certificate() as object.HostCertificateInfo
   781  func (s *Server) CertificateInfo() *object.HostCertificateInfo {
   782  	info := new(object.HostCertificateInfo)
   783  	info.FromCertificate(s.Certificate())
   784  	return info
   785  }
   786  
   787  // CertificateFile returns a file name, where the file contains the PEM encoded Server.Certificate.
   788  // The temporary file is removed when Server.Close() is called.
   789  func (s *Server) CertificateFile() (string, error) {
   790  	if s.caFile != "" {
   791  		return s.caFile, nil
   792  	}
   793  
   794  	f, err := os.CreateTemp("", "vcsim-")
   795  	if err != nil {
   796  		return "", err
   797  	}
   798  	defer f.Close()
   799  
   800  	s.caFile = f.Name()
   801  	cert := s.Certificate()
   802  	return s.caFile, pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
   803  }
   804  
   805  // proxy tunnels SDK requests
   806  func (s *Server) proxy(w http.ResponseWriter, r *http.Request) {
   807  	if r.Method != http.MethodConnect {
   808  		http.Error(w, "", http.StatusMethodNotAllowed)
   809  		return
   810  	}
   811  
   812  	dst, err := net.Dial("tcp", s.URL.Host)
   813  	if err != nil {
   814  		http.Error(w, err.Error(), http.StatusBadGateway)
   815  		return
   816  	}
   817  	w.WriteHeader(http.StatusOK)
   818  
   819  	src, _, err := w.(http.Hijacker).Hijack()
   820  	if err != nil {
   821  		http.Error(w, err.Error(), http.StatusBadRequest)
   822  		return
   823  	}
   824  
   825  	go io.Copy(src, dst)
   826  	go func() {
   827  		_, _ = io.Copy(dst, src)
   828  		_ = dst.Close()
   829  		_ = src.Close()
   830  	}()
   831  }
   832  
   833  // StartTunnel runs an HTTP proxy for tunneling SDK requests that require TLS client certificate authentication.
   834  func (s *Server) StartTunnel() error {
   835  	tunnel := &http.Server{
   836  		Addr:    fmt.Sprintf("%s:%d", s.URL.Hostname(), s.Tunnel),
   837  		Handler: http.HandlerFunc(s.proxy),
   838  	}
   839  
   840  	l, err := net.Listen("tcp", tunnel.Addr)
   841  	if err != nil {
   842  		return err
   843  	}
   844  
   845  	if s.Tunnel == 0 {
   846  		s.Tunnel = l.Addr().(*net.TCPAddr).Port
   847  	}
   848  
   849  	// Set client proxy port (defaults to vCenter host port 80 in real life)
   850  	q := s.URL.Query()
   851  	q.Set("GOVMOMI_TUNNEL_PROXY_PORT", strconv.Itoa(s.Tunnel))
   852  	s.URL.RawQuery = q.Encode()
   853  
   854  	go tunnel.Serve(l)
   855  
   856  	return nil
   857  }
   858  
   859  // Close shuts down the server and blocks until all outstanding
   860  // requests on this server have completed.
   861  func (s *Server) Close() {
   862  	s.Server.Close()
   863  	if s.caFile != "" {
   864  		_ = os.Remove(s.caFile)
   865  	}
   866  }
   867  
   868  var (
   869  	vim25MapType = types.TypeFunc()
   870  )
   871  
   872  func defaultMapType(name string) (reflect.Type, bool) {
   873  	typ, ok := vim25MapType(name)
   874  	if !ok {
   875  		// See TestIssue945, in which case Go does not resolve the namespace and name == "ns1:TraversalSpec"
   876  		// Without this hack, the SelectSet would be all nil's
   877  		kind := strings.SplitN(name, ":", 2)
   878  		if len(kind) == 2 {
   879  			typ, ok = vim25MapType(kind[1])
   880  		}
   881  	}
   882  	return typ, ok
   883  }
   884  
   885  // Element can be used to defer decoding of an XML node.
   886  type Element struct {
   887  	start xml.StartElement
   888  	inner struct {
   889  		Content string `xml:",innerxml"`
   890  	}
   891  	typeFunc func(string) (reflect.Type, bool)
   892  }
   893  
   894  func (e *Element) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
   895  	e.start = start
   896  
   897  	return d.DecodeElement(&e.inner, &start)
   898  }
   899  
   900  func (e *Element) decoder() *xml.Decoder {
   901  	decoder := xml.NewDecoder(strings.NewReader(e.inner.Content))
   902  	decoder.TypeFunc = e.typeFunc // required to decode interface types
   903  	return decoder
   904  }
   905  
   906  func (e *Element) Decode(val interface{}) error {
   907  	return e.decoder().DecodeElement(val, &e.start)
   908  }
   909  
   910  // UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type
   911  func UnmarshalBody(typeFunc func(string) (reflect.Type, bool), data []byte) (*Method, error) {
   912  	body := &Element{typeFunc: typeFunc}
   913  	req := soap.Envelope{
   914  		Header: &soap.Header{
   915  			Security: new(Element),
   916  		},
   917  		Body: body,
   918  	}
   919  
   920  	err := xml.Unmarshal(data, &req)
   921  	if err != nil {
   922  		return nil, fmt.Errorf("xml.Unmarshal: %s", err)
   923  	}
   924  
   925  	var start xml.StartElement
   926  	var ok bool
   927  	decoder := body.decoder()
   928  
   929  	for {
   930  		tok, derr := decoder.Token()
   931  		if derr != nil {
   932  			return nil, fmt.Errorf("decoding: %s", derr)
   933  		}
   934  		if start, ok = tok.(xml.StartElement); ok {
   935  			break
   936  		}
   937  	}
   938  
   939  	if !ok {
   940  		return nil, fmt.Errorf("decoding: method token not found")
   941  	}
   942  
   943  	kind := start.Name.Local
   944  	rtype, ok := typeFunc(kind)
   945  	if !ok {
   946  		return nil, fmt.Errorf("no vmomi type defined for '%s'", kind)
   947  	}
   948  
   949  	val := reflect.New(rtype).Interface()
   950  
   951  	err = decoder.DecodeElement(val, &start)
   952  	if err != nil {
   953  		return nil, fmt.Errorf("decoding %s: %s", kind, err)
   954  	}
   955  
   956  	method := &Method{Name: kind, Header: *req.Header, Body: val}
   957  
   958  	field := reflect.ValueOf(val).Elem().FieldByName("This")
   959  
   960  	method.This = field.Interface().(types.ManagedObjectReference)
   961  
   962  	return method, nil
   963  }