001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package org.apache.hadoop.util; 020 021 import java.io.DataInput; 022 import java.io.IOException; 023 024 import org.apache.hadoop.ipc.RPC; 025 import org.apache.hadoop.ipc.protobuf.IpcConnectionContextProtos.IpcConnectionContextProto; 026 import org.apache.hadoop.ipc.protobuf.IpcConnectionContextProtos.UserInformationProto; 027 import org.apache.hadoop.ipc.protobuf.RpcPayloadHeaderProtos.*; 028 import org.apache.hadoop.security.SaslRpcServer.AuthMethod; 029 import org.apache.hadoop.security.UserGroupInformation; 030 031 public abstract class ProtoUtil { 032 033 /** 034 * Read a variable length integer in the same format that ProtoBufs encodes. 035 * @param in the input stream to read from 036 * @return the integer 037 * @throws IOException if it is malformed or EOF. 038 */ 039 public static int readRawVarint32(DataInput in) throws IOException { 040 byte tmp = in.readByte(); 041 if (tmp >= 0) { 042 return tmp; 043 } 044 int result = tmp & 0x7f; 045 if ((tmp = in.readByte()) >= 0) { 046 result |= tmp << 7; 047 } else { 048 result |= (tmp & 0x7f) << 7; 049 if ((tmp = in.readByte()) >= 0) { 050 result |= tmp << 14; 051 } else { 052 result |= (tmp & 0x7f) << 14; 053 if ((tmp = in.readByte()) >= 0) { 054 result |= tmp << 21; 055 } else { 056 result |= (tmp & 0x7f) << 21; 057 result |= (tmp = in.readByte()) << 28; 058 if (tmp < 0) { 059 // Discard upper 32 bits. 060 for (int i = 0; i < 5; i++) { 061 if (in.readByte() >= 0) { 062 return result; 063 } 064 } 065 throw new IOException("Malformed varint"); 066 } 067 } 068 } 069 } 070 return result; 071 } 072 073 074 /** 075 * This method creates the connection context using exactly the same logic 076 * as the old connection context as was done for writable where 077 * the effective and real users are set based on the auth method. 078 * 079 */ 080 public static IpcConnectionContextProto makeIpcConnectionContext( 081 final String protocol, 082 final UserGroupInformation ugi, final AuthMethod authMethod) { 083 IpcConnectionContextProto.Builder result = IpcConnectionContextProto.newBuilder(); 084 if (protocol != null) { 085 result.setProtocol(protocol); 086 } 087 UserInformationProto.Builder ugiProto = UserInformationProto.newBuilder(); 088 if (ugi != null) { 089 /* 090 * In the connection context we send only additional user info that 091 * is not derived from the authentication done during connection setup. 092 */ 093 if (authMethod == AuthMethod.KERBEROS) { 094 // Real user was established as part of the connection. 095 // Send effective user only. 096 ugiProto.setEffectiveUser(ugi.getUserName()); 097 } else if (authMethod == AuthMethod.DIGEST) { 098 // With token, the connection itself establishes 099 // both real and effective user. Hence send none in header. 100 } else { // Simple authentication 101 // No user info is established as part of the connection. 102 // Send both effective user and real user 103 ugiProto.setEffectiveUser(ugi.getUserName()); 104 if (ugi.getRealUser() != null) { 105 ugiProto.setRealUser(ugi.getRealUser().getUserName()); 106 } 107 } 108 } 109 result.setUserInfo(ugiProto); 110 return result.build(); 111 } 112 113 public static UserGroupInformation getUgi(IpcConnectionContextProto context) { 114 if (context.hasUserInfo()) { 115 UserInformationProto userInfo = context.getUserInfo(); 116 return getUgi(userInfo); 117 } else { 118 return null; 119 } 120 } 121 122 public static UserGroupInformation getUgi(UserInformationProto userInfo) { 123 UserGroupInformation ugi = null; 124 String effectiveUser = userInfo.hasEffectiveUser() ? userInfo 125 .getEffectiveUser() : null; 126 String realUser = userInfo.hasRealUser() ? userInfo.getRealUser() : null; 127 if (effectiveUser != null) { 128 if (realUser != null) { 129 UserGroupInformation realUserUgi = UserGroupInformation 130 .createRemoteUser(realUser); 131 ugi = UserGroupInformation 132 .createProxyUser(effectiveUser, realUserUgi); 133 } else { 134 ugi = org.apache.hadoop.security.UserGroupInformation 135 .createRemoteUser(effectiveUser); 136 } 137 } 138 return ugi; 139 } 140 141 static RpcKindProto convert(RPC.RpcKind kind) { 142 switch (kind) { 143 case RPC_BUILTIN: return RpcKindProto.RPC_BUILTIN; 144 case RPC_WRITABLE: return RpcKindProto.RPC_WRITABLE; 145 case RPC_PROTOCOL_BUFFER: return RpcKindProto.RPC_PROTOCOL_BUFFER; 146 } 147 return null; 148 } 149 150 151 public static RPC.RpcKind convert( RpcKindProto kind) { 152 switch (kind) { 153 case RPC_BUILTIN: return RPC.RpcKind.RPC_BUILTIN; 154 case RPC_WRITABLE: return RPC.RpcKind.RPC_WRITABLE; 155 case RPC_PROTOCOL_BUFFER: return RPC.RpcKind.RPC_PROTOCOL_BUFFER; 156 } 157 return null; 158 } 159 160 public static RpcPayloadHeaderProto makeRpcPayloadHeader(RPC.RpcKind rpcKind, 161 RpcPayloadOperationProto operation, int callId) { 162 RpcPayloadHeaderProto.Builder result = RpcPayloadHeaderProto.newBuilder(); 163 result.setRpcKind(convert(rpcKind)).setRpcOp(operation).setCallId(callId); 164 return result.build(); 165 } 166 }