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

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